如何选择可能使用 awk/sed 多次出现的两个标记模式之间的行
Posted
技术标签:
【中文标题】如何选择可能使用 awk/sed 多次出现的两个标记模式之间的行【英文标题】:How to select lines between two marker patterns which may occur multiple times with awk/sed 【发布时间】:2013-08-02 01:31:03 【问题描述】:使用awk
或sed
如何选择出现在两个不同标记模式之间的行?可能有多个部分标有这些模式。
例如: 假设文件包含:
abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu
起始模式为abc
,结束模式为mno
所以,我需要输出为:
def1
ghi1
jkl1
def2
ghi2
jkl2
我正在使用 sed 匹配一次模式:
sed -e '1,/abc/d' -e '/mno/,$d' <FILE>
sed
或 awk
有没有办法重复执行直到文件结束?
【问题讨论】:
【参考方案1】:在必要时使用带有标志的awk
来触发打印:
$ awk '/abc/flag=1;next/mno/flag=0flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2
这是如何工作的?
/abc/
匹配包含此文本的行,/mno/
匹配。
当找到文本 abc
时,/abc/flag=1;next
设置 flag
。然后,它跳过该行。
当找到文本 mno
时,/mno/flag=0
取消设置 flag
。
最后的flag
是具有默认操作的模式,即print $0
:如果flag
等于1,则打印该行。
有关更详细的描述和示例,以及显示或不显示模式的情况,请参阅How to select lines between two patterns?。
【讨论】:
如果你想打印模式之间的所有内容,包括,那么你可以使用awk '/abc/a=1/mno/print;a=0a' file
。
是的,@scai!甚至awk '/abc/a=1 a; /mno/a=0' file
- 有了这个,将a
条件放在/mno/
之前,我们让它在设置a=0
之前评估该行为真(并打印它)。这样我们就可以避免写print
。
@scai @fedorqui 对于包括模式输出,你可以做awk '/abc/,/mno/' file
@hkasera awk '/abc/flag=1/mno/flag=0flag' file
应该做。
@EirNym 这是一个奇怪的场景,可以通过非常不同的方式处理:您想打印哪些行?可能awk 'flag; /PAT1/flag=1; next /PAT1/flag=0' file
会成功。【参考方案2】:
使用sed
:
sed -n -e '/^abc$/,/^mno$/ /^abc$/d; /^mno$/d; p; '
-n
选项表示默认不打印。
该模式查找仅包含abc
到仅包含mno
的行,然后执行 ...
中的操作。第一个动作删除abc
行;第二个mno
行; p
打印剩余的行。您可以根据需要放松正则表达式。 abc
..mno
范围之外的任何行都不会打印。
【讨论】:
@JonathanLeffler 我能知道使用-e
的目的是什么
@KasunSiyambalapitiya:主要是因为我喜欢使用它。形式上,它指定下一个参数是(部分)sed
应该执行的脚本。如果您想要或需要使用多个参数来包含整个脚本,那么您必须在每个此类参数之前使用-e
;否则,它是可选的(但明确)。
不错! (我更喜欢 sed 而不是 awk。)使用复杂的正则表达式时,不必重复它们会很好。是否可以删除“选定”范围的第一行/最后一行?还是先将d
应用于第一场比赛之前的所有行,然后将另一个d
应用于从第二场比赛开始的所有行?
(回复我自己的评论。)如果只有一个部分要剪切,我可以暂时解决这个问题,例如对于 LaTeX,使用 sed -n '1,/\\begindocument/d;/\\enddocument/d;p'
。 (这有点作弊,因为第二部分直到文档末尾都没有删除,而且我不知道如何按照 OP 的要求剪切多个部分。)
@JonathanLeffler 插入$
标记的原因是什么,如/^abc$
等【参考方案3】:
这可能对你有用(GNU sed):
sed '/^abc$/,/^mno$///!b;d' file
删除除以abc
和mno
开头的行之外的所有行
【讨论】:
!d;//d
Golfs 2 个字符更好:-) ***.com/a/31380266/895245
这太棒了。 //!b
阻止 abc
和 mno
包含在输出中,但我不知道如何。你能解释一下吗?
@Brendan 指令 //!b
读取当前行是否不是与范围匹配的行之一,中断并打印这些行,否则将删除所有其他行。【参考方案4】:
sed '/^abc$/,/^mno$/!d;//d' file
打两个字比ppotong's//!b;d
好
空的正斜杠//
表示:“重用上次使用的正则表达式”。和命令做的一样,比较好理解:
sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file
这个seems to be POSIX:
如果 RE 为空(即未指定模式),sed 的行为就像指定了最后一个应用的命令(作为地址或作为替代命令的一部分)中使用的最后一个 RE。
【讨论】:
我认为第二个解决方案最终会一无所获,因为第二个命令也是一个范围。然而,第一个荣誉。 @potong 真的!我必须更多地研究为什么第一个有效。谢谢!【参考方案5】:从上一个响应的链接中,为我做的那个,在 Solaris 上运行 ksh
,是这样的:
sed '1,/firstmatch/d;/secondmatch/,$d'
1,/firstmatch/d
:从第 1 行到第一次找到firstmatch
,删除。
/secondmatch/,$d
:从secondmatch
的第一次出现到文件结束,删除。
分号分隔两个命令,按顺序执行。
【讨论】:
只是好奇,为什么范围限制器(1,
)出现在/firstmatch/
之前?我猜这也可以表述为'/firstmatch/1,d;/secondmatch,$d'
?
使用 "1,/firstmatch/d" 表示“从第 1 行到第一次找到 'firstmatch',删除”。而对于“/secondmatch/,$d”,您会说“从第一次出现 'secondmatch' 到文件末尾,删除”。分号分隔两个命令,按顺序执行。【参考方案6】:
这样的东西对我有用:
文件.awk:
BEGIN
record=0
/^abc$/
record=1
/^mno$/
record=0;
print "s="s;
s=""
!/^abc|mno$/
if (record==1)
s = s"\n"$0
使用:awk -f file.awk data
...
编辑:O_o fedorqui 解决方案比我的更好/更漂亮。
【讨论】:
在 GNU awk 中if (record=1)
应该是 if (record==1)
,即双倍 =
- 请参阅 gawk comparison operators【参考方案7】:
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file
【讨论】:
很高兴知道 perl 等价物,因为它是 awk 和 sed 的一个很好的替代品。【参考方案8】:Don_crissti 来自Show only text between 2 matching pattern 的回答?
firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile
这比 AWK 的应用程序效率高得多,请参阅here。
【讨论】:
我不认为在这里链接时间比较有多大意义,因为问题的要求完全不同,因此解决方案。 我不同意,因为我们应该有一些标准来比较答案。只有少数有 SED 应用程序。【参考方案9】:我尝试使用awk
打印两个模式之间的线条,而pattern2 也匹配pattern1。并且 pattern1 线也应该被打印出来。
例如 来源
package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj
应该有一个输出
package BBB
ddd
eee
pattern1 是package BBB
,pattern2 是package \w*
。请注意,CCC
不是一个已知值,因此无法逐字匹配。
在这种情况下,@scai 的 awk '/abc/a=1/mno/print;a=0a' file
和 @fedorqui 的 awk '/abc/a=1 a; /mno/a=0' file
都不适合我。
终于被awk '/package BBB/flag=1;print;next/package \w*/flag=0flag' file
解决了,哈哈
在awk '/package BBB/flag=1;print;nextflag;/package \w*/flag=0' file
的努力下,也打印了pattern2行,也就是
package BBB
ddd
eee
package CCC
【讨论】:
【参考方案10】:这也可以通过对标志的逻辑操作和递增/递减操作来完成:
awk '/mno/&&--f||f||/abc/&&f++' file
【讨论】:
以上是关于如何选择可能使用 awk/sed 多次出现的两个标记模式之间的行的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 shell(awk、sed 等)删除文件中的前两列
awk && sed ====积累取ip以及sed 查找替换