74

问题:我想在包含匹配模式的行之后直接打印一行。

我的版本sed不会采用以下语法(它会爆炸+1p),这似乎是一个简单的解决方案:

sed -n '/ABC/,+1p' infile

我认为awk进行多行处理会更好,但我不知道该怎么做。

4

8 回答 8

167

永远不要使用“模式”这个词,因为它非常模棱两可。始终使用“字符串”或“正则表达式”(或在 shell 中的“通配模式”),无论你真正的意思是什么。

您想要的具体答案是:

awk 'f{print;f=0} /regexp/{f=1}' file

或者在正则表达式之后专门针对第 N 条记录的更一般的解决方案(下面的成语“c”):

awk 'c&&!--c; /regexp/{c=1}' file

以下成语描述了如何在给定特定正则表达式的情况下选择一系列记录进行匹配:

a)打印一些正则表达式的所有记录:

awk '/regexp/{f=1}f' file

b)在一些正则表达式之后打印所有记录:

awk 'f;/regexp/{f=1}' file

c) 在一些正则表达式之后打印第 N 条记录:

awk 'c&&!--c;/regexp/{c=N}' file

d) 在一些正则表达式之后打印除第 N 条记录之外的每条记录:

awk 'c&&!--c{next}/regexp/{c=N}1' file

e) 在一些正则表达式之后打印 N 条记录:

awk 'c&&c--;/regexp/{c=N}' file

f) 在一些正则表达式之后打印除 N 条记录之外的每条记录:

awk 'c&&c--{next}/regexp/{c=N}1' file

g) 从一些正则表达式打印 N 条记录:

awk '/regexp/{c=N}c&&c--' file

我将变量名称从“found”的“f”更改为“count”的“c”,因为这更能表达变量的实际含义。

f是 的缩写found。它是一个布尔标志,当我在输入 ( ) 中找到与正则表达式 regexp 匹配的字符串时,我将其设置为 1 (true /regexp/{f=1})。您在每个脚本中看到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达到零并继续递减,所以它经常环绕并再次变为正数。

于 2013-07-28T23:35:01.800 回答
46

你感兴趣的是那场比赛之后的台词,对吧?在 sed 中,可以这样完成:

sed -n '/ABC/{n;p}' infile

或者,grep 的A选项可能是您正在寻找的。

-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

希望有帮助。

于 2013-07-28T13:27:55.227 回答
6

我需要在模式之后打印所有行(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' 

上面的脚本删除从第一行开始到(包括)与模式匹配的行的每一行。打印之后的所有行。

于 2016-06-24T19:16:42.787 回答
3

awk 版本:

awk '/regexp/ { getline; print $0; }' filetosearch
于 2013-07-28T15:56:46.903 回答
1

这可能对您有用(GNU sed):

sed -n ':a;/regexp/{n;h;p;x;ba}' file

使用 seds 类似 grep 的选项-n,如果当前行包含所需的正则表达式,则将当前行替换为下一行,将该行复制到保留空间 (HS),打印该行,交换 HS 的模式空间 (PS) 并重复.

于 2017-08-22T23:05:05.140 回答
1

如果匹配线实际上sed -n '/pattern/{n;p}' filename会失败:patterncontinuous

$ 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

解释:

  1. x;: 在输入的每一行开头,使用x命令交换pattern space&中的内容hold space
  2. /_/{x;p;x};: ifpattern spacehold space实际包含的_(这只是indicator表示最后一行是否匹配pattern),然后用于交换tox的实际内容,用于打印,并恢复此操作。current linepattern spacepcurrent linex
  3. x: 恢复 和 中的pattern space内容hold space
  4. /pattern/!s/.*//:如果current line不匹配pattern,这意味着我们不应该打印下一行,然后使用s/.*//命令删除所有内容pattern space
  5. /pattern/s/.*/_/: if current linematchespattern表示我们应该打印下一行,那么我们需要设置aindicator来告诉sed打印下一行,所以使用s/.*/_/将所有内容替换pattern space为a _(第二个命令将使用它来判断最后一行是否匹配pattern或不)。
  6. hhold space:用 中的内容覆盖pattern space; 那么,里面的内容hold space^_$ 表示current line匹配的pattern,或者^$表示current line不匹配的pattern
  7. 第五步和第六步不能互换,因为之后s/.*/_/pattern space不能匹配/pattern/,所以s/.*//必须执行!
于 2017-06-06T06:19:23.873 回答
1

如果模式匹配,则将下一行复制到模式缓冲区中,删除一个返回,然后退出——副作用是打印。

sed '/pattern/ { N; s/.*\n//; q }; d'
于 2016-01-11T18:35:13.047 回答
0

管道一些 grep 可以做到(它在 POSIX shell 和 BusyBox 下运行):

cat my-file | grep -A1 my-regexp | grep -v -- '--' | grep -v my-regexp
  1. -v将显示不匹配的行
  2. --由 grep 打印以分隔每个匹配项,所以我们也跳过它
于 2020-07-23T12:01:04.580 回答