获取以下文件:
$ cat f1
stu vwx yza
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu
pqr stu vwx
stu vwx yza
打印从第一行开始包含到abc
的所有行第一个包含mno
GNU sed
:
$ sed -n '/abc/,/mno/p' f1
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
我怎样才能打印所有行直到最后一个包含mno
,例如我怎样才能得到以下结果:
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu
换句话说,有没有办法让GNUsed
的范围选择变得贪婪?
更新
在我的设置中:
- 如果
mno
丢失,它应该打印出所有内容,直到文件末尾。 mno
不能发生在第一个之前abc
。- 总是至少有一个
abc
,abc
和mno
永远不会在同一行
编辑
我只是stu vwx yza
在开头添加了一条虚拟行,以便文件不会以包含的行开头abc
(以避免从第一行开始的解决方案 - 它们应该从abc
其中包含的第一行开始)
答案1
sed '/abc/,$!d;0,/mno/b;:1;/mno/b;$d;N;b1' file
工作算法:
使用两个地址范围。
第一个/abc/,$!d;
删除第一个模式匹配之前的所有内容。
第二个0,/mno/b;
直到与模式匹配为止/mno/
,将每个行缓冲区(模式空间)发送到绕过剩余脚本的输出,从而防止在文件中找不到模式时删除。
脚本的其余部分:1;/mno/b;$d;N;b1
循环工作。在编辑器缓冲区中,行会被追加,直到出现模式匹配为止。如果/mno/
遇到模式,整个缓冲区将被发送到输出,绕过脚本的其余部分。如果没有匹配,则删除缓冲区的最后一行。
答案2
awk
如果有的话你可以使用。您可以标记模式开始和模式停止的行,并在文件的一次传递中打印这些行(涉及存储从第一行开始abc
到缓冲区中最后一行的行)
awk '/abc/ && !start {
start = NR
}
/mno/ {
stop = NR
}
start { line[NR] = $0 }
END {
if ( !stop ) {
stop = NR
}
for ( s = start; s <= stop; s++ )
print line[s]
}' file
请注意,当起始图案不存在时,这将不起作用,仅打印一系列空白行。
答案3
另一种awk
缓冲较少的解决方案:
awk '!f&&/abc/{f=1} f==1; f==2{buf=buf $0 ORS} f&&/mno/{f=2; printf "%s",buf; buf=""}' input.txt
- 这将打印从第一次出现
abc
(将标志设置f
为 1)开始直到(包括第一次出现)的所有内容mno
。f==1
规则块之外的语句指示只要awk
设置为 就打印当前f
行1
。 - 然后,每次出现 后
mno
(f
现在值为 2)所有行的内容都存储在缓冲区 中buf
,该缓冲区在下一次出现 时被打印并清除mno
。确保我们正确应对第一个mno
发生的情况前首先abc
,我们要求f
在应用该逻辑之前至少将其设置为 1。
因此,它最多会存储两次出现之间的文本mno
,或者最后一次出现mno
和文件结束之间的文本(只是后一部分永远不会被打印)。
如果你想用内存效率来交换速度,下面的两遍方法将根本不依赖缓冲:
awk 'FNR==NR{if (/abc/&&!start) {start=FNR} else if (/mno/) {end=FNR}; next} FNR>=start&&(!end||FNR<=end)' input.txt input.txt
这将处理文件两次(因此将其指定为参数两次)。
- 第一次,当
FNR
每个文件的行计数器等于NR
全局行计数器时,我们查找第一次出现的abc
和 最后一次出现的mno
,并将它们的行号分别存储在start
和中end
。 - 在第二遍中,只要计数器
FNR
位于(并包括)start
end之间end
(或者只是它大于/等于未设置的start
情况),我们就打印行。end
答案4
您可以收集所有行,从abc
保留空间中的行开始,然后使用贪婪性质.*
删除最后一个之后的所有内容mno
:
sed '/abc/,$!d;H;$!d;x;s/\n//;s/\(.*mno[^\n]*\).*/\1/'
/abc/,$!d
是d
删除第一abc
行之前的所有内容(或整个文件,如果abc
根本没有行)H;$!d
是在保留空间中收集整个文件的经典模式(请注意,这对于非常大的文件可能是一个问题)- 我们
x
更改缓冲区而不是使用g
以避免复制大缓冲区 s/\n//
删除开头错误的换行符,该换行符是通过附加到空的保留空间而产生的s/\(.*mno[^\n]*\n\).*/\1/
删除之后的所有内容最后的mno
行(或者根据要求打印整个剩余文件,如果没有mno
行)。请注意,这[^\n]
不是 POSIX,并且仅适用于某些版本,例如 GNUsed
。