获取以下文件:
$ 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
)
awk
如果它是一个选项,你可以使用。您可以标记模式开始和模式停止的行,并在文件的一次传递中打印这些行(涉及存储从第一行开始的行,abc
直到缓冲区中的最后一行)请注意,当起始模式不存在时,这将不起作用,仅打印一系列空白行。
工作算法:
使用两个地址范围。
第一个
/abc/,$!d;
删除直到第一个模式匹配的所有内容。与模式匹配的第二个,将每一行缓冲区(模式空间)发送到输出绕过剩余的脚本,从而防止在文件中找不到模式时删除。 脚本的其余部分循环工作。在编辑器缓冲区中,添加行直到发生模式匹配。如果遇到模式,则将整个缓冲区发送到输出,绕过脚本的其余部分。如果不匹配,则在最后一行删除缓冲区。
0,/mno/b;
/mno/
:1;/mno/b;$d;N;b1
/mno/
另一种
awk
缓冲较少的解决方案:abc
(将标志设置f
为 1)开始的所有内容,包括第一次出现mno
.f==1
规则块之外的语句指示只要awk
设置为 就打印当前f
行1
。mno
(f
现在的值为 2)存储在缓冲区中buf
,该缓冲区在下一次出现时打印并清除mno
。为了确保我们正确处理 firstmno
出现在 first之前的情况abc
,我们要求f
在应用该逻辑之前至少将其设置为 1。因此,它将最多存储两次出现之间的文本
mno
,或者最后一次出现mno
和文件结尾(只有后一部分永远不会被打印)。如果你想用内存效率交换速度,下面的两遍方法根本不依赖缓冲:
这将处理文件两次(因此它被指定两次作为参数)。
FNR
每个文件的行计数器等于NR
全局行计数器时,我们查找 的第一次出现abc
和最后一次出现mno
,并将它们的行号分别存储在start
和end
中。FNR
计数器在(并包括)start
结束之间(或者只是如果end
它大于/等于未设置),我们就会打印行。start
end
我不认为 sed 可以变得贪婪,不。一种可能的解决方法,简单但效率低,是处理文件两次。一次获取行范围,另一个进行打印。例如:
或者,如果您想处理缺少一种或两种模式的情况:
如果
abc
没有找到,它将从文件的开头打印。如果mnc
未找到,它将从abc
(或开头,如果abc
不存在)打印到结尾。如果没有找到任何模式,它当然不会打印任何内容。收集行,直到一个
mno
序列使它们准备好打印:/abc/,$!d
d
删除除从第一abc
行到结尾的范围之外的所有内容。这也处理了根本没有的情况abc
。:loop
./mno/{p;d;}
如果mno
模式空间中有,请p
重新开始。$d
如果我们到达没有 的最后一行mno
,d
则删除缓冲区中的所有内容。不幸的是,这意味着没有输出,如果根本没有mno
的话。N
ext 行并继续循环。您可以收集所有行,从
abc
保留空间中的行开始,然后使用贪婪的性质.*
删除最后一个之后的所有内容mno
:/abc/,$!d
是d
删除第一行之前的所有内容abc
(或整个文件,如果根本没有abc
行)H;$!d
是在保存空间中收集整个文件的经典模式(请注意,这对于非常大的文件可能是一个问题)x
更改缓冲区而不是使用g
以避免复制大缓冲区s/\n//
在开头删除错误的换行符,通过附加到空的保留空间产生s/\(.*mno[^\n]*\n\).*/\1/
删除最后mno
一行之后的所有内容(或根据要求打印整个剩余文件,如果没有mno
行)。请注意,这[^\n]
不是 POSIX,仅适用于 GNU 等某些版本sed
。使用 Raku(以前称为 Perl_6)
样本输入:
样本输出:
请注意,当输入第 11 行样本输入时,样本输出是第 2 到第 10 行的返回。此外,当样本输入被截断为仅第 1 到第 10 行(即
mno
在最后一行)时,上面的 Raku 代码仍然(正确地)返回第 2 到第 10 行。感谢 @ImHere 和 @ChennyStar 在评论中鼓励我提出更强大的 Raku 解决方案。
https://raku.org
使用
GNU sed
注意:根据 OP,第一行 abc 没有 mno,因此我们可以在下面的 sed 代码中利用这一事实。在这种方法中,我们使用 slurp 模式
-z
读取模式空间中的完整文件。然后我们删除直到包含 abc 的第一行之前。之后使用正则表达式的贪婪到达最后一个 mno 行。还有一种方法是两遍方法,我们记录第一个 abc 行和最后一个 mno 行的行号。如果没有 mno 存在,我们在它的位置填写 $。然后使用这两个数字我们构造一个 sed 命令 begin,end
p
;endq
我们可以使用
perl
slurp 文件,然后整个文件是一个长字符串,我们从两端烧录并在满足条件时停止。以下是使用 sed 编辑器获得所需输出的更多方法。
另一种方法是我们只在看到 /mno/ 之前将行存储。此时我们翻转并打印保留的内容。
这是使用通用 itertools 模块 groupby 方法完成工作的 Python 方式。