我需要解析一个文件,并且希望打印两个特定行之间的一段数据。从“范围开始”到“范围结束”,但前提是“范围结束”存在。
如果源文件是:
[This is the start] of some data
this is information
this is more information
This is does not contain the ending required
[This is the start] of some other data
this is info I want
this is info I want
[This is the ending I was looking for]
它应该打印:
[This is the start] of some other data
this is info I want
this is info I want
[This is the ending I was looking for]
使用 grep 我已经能够找到我需要的数据并向上打印,但只能打印固定的行数。
鉴于数据行数不是恒定的,有没有一种方法可以使用 grep 或 sed,从末尾行开始查找给定字符串的下一次出现并捕获我想要的特定范围?
数据段的“范围开始”应与“范围开始”和“范围结束”点之间的任何数据一起打印,“范围结束”匹配决定是否应打印整个行范围。如果范围(数据段)没有指定的结尾,则不应打印它。如果多个段有一个终点,则应打印包含终点的所有段。不存在输入文件有结尾而没有开头的情况,或者单个开头有多个结尾的情况。
打印两个图案之间(并包括两个图案)之间的线并没有解决我的问题,因为它开始在匹配的第一行上打印并继续打印直到找到第一个结束段。我只需要打印包含指定结束语句的段。
答案1
使用sed
:
$ sed -n '/This is the start/{h;d;}; H; /This is the ending/{x;p;}' file
[This is the start] of some other data
this is info I want
this is info I want
[This is the ending I was looking for]
带注释的sed
脚本:
/This is the start/{ # We have found a start
h; # Overwrite the hold space with it
d; # Delete from pattern space, start next cycle
};
H; # Append all other lines to the hold space
/This is the ending/{ # We have found an ending
x; # Swap pattern space with hold space
p; # Print pattern space
};
该脚本的作用是将所有行保存到“保留空间”(中的通用缓冲区sed
)中,但是一旦我们找到“起始行”,我们就会重置该空间。当找到“结束行”时,将打印保存的数据。
如果在“起始线”之前找到“结束线”,并且如果找到两条“结束线”而中间没有“起始线”,则此情况会中断。
awk
与上述程序执行相同过程的程序sed
:
$ awk '/This is the start/ { hold = $0; next }
{ hold = hold ORS $0 }
/This is the ending/ { print hold }' file
(与上面相同的输出)
答案2
对于多个START
andEND
模式,您可以这样做:
sed 'H;/START/h;/END/!d;x;/START/!d' infile
这将无条件地在旧缓冲区中累积行,每次遇到一行时H
都会覆盖它(即仅保留最近一行的数据),如果模式空间不包含行则删除模式空间(循环重新启动)这里)否则 e更改缓冲区并再次删除模式空间,这次如果它不包含.剩下的都是自动打印的。h
START
START
d
END
x
d
START
答案3
用于tac
反转行的顺序
如果您使用tac
反转文件 - 首先打印最后一行,依此类推 - 那么您可以提取从结束模式到开始模式的区域。然后,tac
再次使用按正向顺序打印输出行。
tac file.txt | awk '/^\[This is the ending I was looking for]/,/^\[This is the start]/ { print $0 }' | tac
相同的代码,经过格式化以更好地适应屏幕:
tac file.txt | \
awk '/^\[This is the ending I was looking for]/,/^\[This is the start]/ { print $0 }' | \
tac
{ print $0 }
在此特定命令中不需要,awk
因为它是默认行为:
tac file.txt | \
awk '/^\[This is the ending I was looking for]/,/^\[This is the start]/' | \
tac
不幸的是,如果您使用的是 Mac,tac
默认情况下不会安装。
答案4
使用 awk 的解决方案是:
rstart='^[[]This is the start[]]'
rend='[[]This is the ending I was looking for[]]'
awk '$0~rstart{i=1;a=""}
$0~rstart,$0~rend && i==1 {a = a ((a=="")?"":ORS) $0}
$0~rend{i=0;print(a)}
' rstart="$rstart" rend="$rend" infile
方括号由 a 和 匹配[[]
,[]]
以避免使用反斜杠\\[
(在某些情况下可能会失败)。
主要思想是使用变量i
(include)作为布尔值来包含或不包含要打印的范围中的每一行。整个范围累积在变量 中a
。如果变量a
不为空 ( ((a=="")?"":ORS)
),则用 ORS(输出记录分隔符)分隔。
这将打印:
[This is the start] of some other data
this is info I want
this is info I want
[This is the ending I was looking for]
如果要求不打印开始和结束标记,则使用相同的代码,但交换第 1 行和第 3 行:
awk '$0~rend{i=0;print(a)}
$0~rstart,$0~rend && i==1 {a = a ((a=="")?"":RS) $0}
$0~rstart{i=1;a=""}
' rstart="$rstart" rend="$rend" infile
将打印:
this is info I want
this is info I want