使用 sed 或 awk 按照匹配模式打印一行
Posted
技术标签:
【中文标题】使用 sed 或 awk 按照匹配模式打印一行【英文标题】:Printing with sed or awk a line following a matching pattern 【发布时间】:2013-07-28 07:53:58 【问题描述】:问题:我想在包含匹配模式的行之后直接打印一行。
我的sed
版本不会采用以下语法(它会在+1p
上崩溃),这似乎是一个简单的解决方案:
sed -n '/ABC/,+1p' infile
我认为awk
进行多行处理会更好,但我不知道该怎么做。
【问题讨论】:
sed.sourceforge.net/sed1line.txt 【参考方案1】:永远不要使用“模式”这个词,因为它非常模棱两可。始终使用“字符串”或“正则表达式”(或在 shell 中的“通配模式”),无论你是什么意思。
你想要的具体答案是:
awk 'fprint;f=0 /regexp/f=1' file
或者在一个正则表达式之后专门化第 N 条记录的更一般的解决方案(下面的成语“c”):
awk 'c&&!--c; /regexp/c=1' file
以下成语描述了如何在给定特定正则表达式的情况下选择一系列记录进行匹配:
a) 打印一些正则表达式中的所有记录:
awk '/regexp/f=1f' file
b) 在一些正则表达式之后打印所有记录:
awk 'f;/regexp/f=1' file
c) 在一些正则表达式之后打印第 N 条记录:
awk 'c&&!--c;/regexp/c=N' file
d) 在某个正则表达式之后打印除第 N 条记录之外的所有记录:
awk 'c&&!--cnext/regexp/c=N1' file
e) 在一些正则表达式之后打印 N 条记录:
awk 'c&&c--;/regexp/c=N' file
f) 在一些正则表达式之后打印除 N 条记录之外的每条记录:
awk 'c&&c--next/regexp/c=N1' file
g) 从某个正则表达式打印 N 条记录:
awk '/regexp/c=Nc&&c--' file
我将变量名从“find”的“f”更改为“count”的“c”,其中 合适,因为这更能表达变量的实际含义。
f
是 found
的缩写。当我在输入 (/regexp/f=1
) 中找到与正则表达式 regexp 匹配的字符串时,它是一个布尔标志,我将其设置为 1 (true)。您在每个脚本中看到 f
的另一个地方是它自己的一个条件,它正在作为条件进行测试,当它为 true 时,会导致 awk 执行其打印当前记录的默认操作。所以输入记录只有在我们看到 regexp 并将f
设置为 1/true 后才会得到输出。
c && c-- foo
的意思是“如果c
不为零,则递减它,如果它仍然不为零,则执行foo
”,因此如果c
从3 开始,那么它将递减到2,然后foo
执行,在下一个输入行 c
现在是 2,所以它会递减到 1,然后 foo
再次执行,在下一个输入行 c
现在是 1 所以它会是减为 0,但这次 foo
将不会被执行,因为 0 是错误条件。我们使用c && c--
而不是只测试c-- > 0
,因此我们不会遇到输入文件很大的情况,其中c
达到零并继续递减,因此它经常回绕并再次变为正数。
【讨论】:
【参考方案2】:after 是您感兴趣的匹配项,对吧?在 sed 中,可以这样完成:
sed -n '/ABC/n;p' infile
或者,grep 的 A 选项可能是您正在寻找的。p>
-A NUM, Print NUM lines of trailing context after matching lines.
例如,给定以下输入文件:
foo
bar
baz
bash
bongo
您可以使用以下内容:
$ grep -A 1 "bar" file
bar
baz
$ sed -n '/bar/n;p' file
baz
希望对您有所帮助。
【讨论】:
注意:n;p
似乎受 GNU sed 支持,但不支持 BSD sed。 (感谢 sed 的回答 chooban。我非常尊重 awk,并且使用过它,但我尽量避免重新学习它的巴洛克语言。(当我需要 awk 时,我使用 perl)。)
更正:我通过添加;
:sed -n /bar/n;p;
成功使用了 BSD sed。也适用于 GNU sed。
如果使用 original sed,您将不得不编写 sed -n '/bar/;n;p;'
,因为
和
的解析方式与字母命令完全相同。【参考方案3】:
我需要在模式之后打印所有行(ok Ed, REGEX),所以我选择了这个:
sed -n '/pattern/,$p' # prints all lines after ( and including ) the pattern
但是因为我想在之后打印所有行(并排除模式)
sed -n '/pattern/,$p' | tail -n+2 # all lines after first occurrence of pattern
我想在您的情况下,您可以在末尾添加 head -1
sed -n '/pattern/,$p' | tail -n+2 | head -1 # prints line after pattern
我真的应该在这个答案中包含 tlwhitec 的评论(因为他们的 sed-strict 方法比我的建议更优雅):
sed '0,/pattern/d'
上面的脚本会删除从第一行开始到(包括)与模式匹配的行为止的每一行。打印之后的所有行。
【讨论】:
第二种情况有 sed-strict 方法:sed '0,/regex/d'
以上正是我建议人们不要使用范围表达式 (/start/,/end/
) 的原因,这意味着您不要使用 sed 执行此类任务,因为它没有变量,所以您只需要剩下的是范围。您想要做的就是排除开始或结束行,它需要您添加管道和其他命令来执行此操作 vs awk '/start/f=1 f; /end/f=0'
- 只需重新排列要打印的块或不打印开始/结束部分,不需要额外的工具或管道.【参考方案4】:
awk 版本:
awk '/regexp/ getline; print $0; ' filetosearch
【讨论】:
谢谢!我忘记了 grep 中的 -A 选项;它与 +1 参数完美配合(不打印具有匹配模式的行)。 当你最意想不到的时候,它会以神秘的方式失败,并且将来很难增强。在决定使用getline
之前,请确保您已准备好并完全理解awk.info/?tip/getline。【参考方案5】:
如果模式匹配,将下一行复制到模式缓冲区,删除一个返回,然后退出——副作用是打印。
sed '/pattern/ N; s/.*\n//; q ; d'
【讨论】:
q
绝不是 GNU 扩展。这是一个标准的sed
命令。
如果您想打印所有匹配项,而不仅仅是第一个匹配项,请使用 p
而不是 q
。【参考方案6】:
如果pattern
匹配continuous
行,实际上sed -n '/pattern/n;p' filename
会失败:
$ seq 15 |sed -n '/1/n;p'
2
11
13
15
预期的答案应该是:
2
11
12
13
14
15
我的解决办法是:
$ sed -n -r 'x;/_/x;p;x;x;/pattern/!s/.*//;/pattern/s/.*/_/;h' filename
例如:
$ seq 15 |sed -n -r 'x;/_/x;p;x;x;/1/!s/.*//;/1/s/.*/_/;h'
2
11
12
13
14
15
解释:
x;
:在输入的每一行开头,使用x
命令交换pattern space
&hold space
中的内容。
/_/x;p;x;
:如果pattern space
,实际上是hold space
,包含_
(这只是一个indicator
,表示最后一行是否匹配pattern
),然后使用x
交换current line
到pattern space
的实际内容,使用p
打印current line
,x
恢复这个操作。
x
:恢复pattern space
和hold space
中的内容。
/pattern/!s/.*//
:如果current line
不匹配pattern
,这意味着我们不应该打印下一行,然后使用s/.*//
命令删除pattern space
中的所有内容。
/pattern/s/.*/_/
:如果current line
匹配pattern
,这意味着我们应该打印下一行,那么我们需要设置一个indicator
来告诉sed
打印下一行,所以用s/.*/_/
替换所有pattern space
中的内容到_
(第二个命令将使用它来判断最后一行是否匹配pattern
)。
h
:用pattern space
中的内容覆盖hold space
;那么hold space
中的内容就是^_$
,表示current line
匹配pattern
,或者^$
,表示current line
不匹配pattern
。
第五步和第六步不能互换,因为s/.*/_/
之后,pattern space
不能匹配/pattern/
,所以s/.*//
必须执行!
【讨论】:
【参考方案7】:这可能对你有用(GNU sed):
sed -n ':a;/regexp/n;h;p;x;ba' file
使用 seds 类似 grep 的选项 -n
如果当前行包含所需的正则表达式,则将当前行替换为下一行,将该行复制到保留空间(HS),打印该行,交换模式空间(PS ) 用于 HS 并重复。
【讨论】:
【参考方案8】:管道一些 grep 可以做到(它在 POSIX shell 和 BusyBox 下运行):
cat my-file | grep -A1 my-regexp | grep -v -- '--' | grep -v my-regexp
-v
将显示不匹配的行
-- 由 grep 打印以分隔每个匹配项,因此我们也跳过它
【讨论】:
以上是关于使用 sed 或 awk 按照匹配模式打印一行的主要内容,如果未能解决你的问题,请参考以下文章