问题:我想在包含匹配模式的行后直接打印一行。
我的版本的sed
不支持以下语法(它会在+1p
处失败),这似乎是一个简单的解决方案:
sed -n '/ABC/,+1p' infile
我认为使用awk
进行多行处理会更好,但我不确定如何做。
在这个上下文中永远不要使用“模式”这个词,因为它很模糊。始终使用“字符串”或“正则表达式”(或在shell中使用“globbing模式”),取决于您真正想要的是什么。有关更多信息,请参见如何查找与模式匹配的文本?
你想要的具体答案是:
awk 'f{print;f=0} /regexp/{f=1}' file
或者专门针对正则表达式的前N个记录(习惯用语“c”如下)进行更一般的解决方案:
awk 'c&&!--c; /regexp/{c=1}' file
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
我将变量名从“f”更改为“c”,表示“count”,这更能表达变量实际的含义。
f
是“found”的缩写。它是一个布尔标记,当我在输入中找到与正则表达式regexp匹配的字符串时(/regexp/{f=1}
),我将其设置为1(真)。在脚本的另一个位置,您会看到每个单独的f
都被测试为条件,并且当为真时,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
减少到0并且继续递减,以至于它再次变成正数。
你感兴趣的是匹配后面的那行,对吗?在sed中,可以通过以下方式实现:
sed -n '/ABC/{n;p}' infile
或者,grep的A选项可能是您正在寻找的内容。
-A NUM, Print NUM lines of trailing context after matching lines.
例如,给定以下输入文件:
For example, given the following input file:
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 不支持。(感谢 chooban 提供 sed 答案。我非常尊敬 awk,并且已经使用它了,但我尽可能避免重新学习其复杂的语言。(当我需要 awk 时,我使用 perl)。) - Mars;
取得了成功:sed -n /bar/{n;p;}
。这个命令在 GNU 版本的 sed 中也可以使用。 - Marssed -n '/bar/{;n;p;}'
,因为{
和}
与字母命令完全相同。 - zwol我需要打印出模式(好的Ed,正则表达式)之后的所有行,所以我采用了这个方法:
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 '0,/regex/d'
。 - tlwhitec/start/,/end/
) 的原因,这意味着你不能使用 sed
来完成此类任务,因为它没有变量,所以你只能使用范围。你想要做的只是排除起始或结束行,而这需要你添加管道和额外的命令来完成,与 awk '/start/{f=1} f; /end/{f=0}'
相比 - 只需重新排列块以打印或不打印开始/结束部分,不需要额外的工具或管道。 - Ed Mortonawk版本:
awk '/regexp/ { getline; print $0; }' filetosearch
getline
之前,请确保您已准备好并完全理解http://awk.info/?tip/getline。 - Ed Mortonsed '/pattern/ { N; s/.*\n//; q }; d'
p
而不是 q
。 - jarnosed -n ':a;/regexp/{n;h;p;x;ba}' file
-n
,如果当前行包含所需的正则表达式,则替换当前行为接下来的一行,将该行复制到保留空间(HS),打印该行,交换模式空间(PS)和HS并重复执行。实际上,如果 pattern
匹配 连续
行,则 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
命令交换模式空间
和保留空间
中的内容。/_/{x;p;x};
:如果模式空间
(实际上是保留空间
)包含_
(这只是一个指示符,表示上一行是否匹配了模式
),则使用x
将当前行
的实际内容交换到模式空间
中,使用p
打印当前行
,并使用x
恢复此操作。x
:恢复模式空间
和保留空间
中的内容。/pattern/!s/.*//
:如果当前行
不匹配模式
,也就是说我们不应该打印下面的行,则使用s/.*//
命令删除模式空间
中的所有内容。/pattern/s/.*/_/
:如果当前行
匹配模式
,也就是说我们应该打印下面的行,则需要设置一个指示符
告诉sed
打印下一行,因此使用s/.*/_/
将模式空间
中的所有内容替换为_
(第二个命令将使用它来判断上一行是否匹配模式
)。h
:用模式空间
中的内容覆盖保留空间
;然后,保留空间
中的内容为^_$
,这意味着当前行
与模式
匹配,或者为^$
,这意味着当前行
不匹配模式
。s/.*/_/
之后,模式空间
无法匹配/pattern/
,因此必须执行s/.*//
!sed
命令将起作用。sed -n -e '/pattern/{n;p;}'
-n
抑制输出(静默模式);
-e
表示 sed 命令(在本例中不需要);
/pattern/
是一个正则表达式搜索,用于查找包含字符组合 pattern
的行(对于仅由“pattern”组成的行,请使用 /^pattern$/
);
n
用下一行替换模式空间;
p
打印;
例如:
seq 10 | sed -n -e '/5/{n;p;}'
pattern
的行后打印一行。如果您只想要第一个,请使用sed -n -e '/pattern/{n;p;q;}'
。这也更有效率,因为不需要读取整个文件。sed
命令将打印出模式后的所有行。sed -n '/pattern/,${/pattern/!p;}
如果以sed
脚本格式化,它将是这样的:
/pattern/,${
/pattern/!p
}
这里是一个简短的示例:
seq 10 | sed -n '/5/,${/5/!p;}'
/pattern/,$
会选择从 pattern
到文件结尾的所有行。
{}
分组下一组命令(类似 C 语言块命令)
/pattern/!p;
打印不匹配 pattern
的行。请注意,在早期版本和某些非 GNU 版本的 sed
中需要使用 ;
。这将把指令转换为排除范围 - sed
范围通常对范围的起始点和结束点都是包容的。
要排除范围的末尾,您可以像这样做:
sed -n '/pattern/,/endpattern/{/pattern/!{/endpattern/d;p;}}
/pattern/,/endpattern/{
/pattern/!{
/endpattern/d
p
}
}
/endpattern/d
从“模式空间”中删除,脚本从顶部重新启动,跳过该行的p
命令。
另一个简洁的例子:
seq 10 | sed -n '/5/,/8/{/5/!{/8/d;p}}'
seq 5 | sed -n --debug '/2/,/4/{/2/!{/4/d;p}}'
输出:
SED PROGRAM:
/2/,/4/ {
/2/! {
/4/ d
p
}
}
INPUT: 'STDIN' line 1
PATTERN: 1
COMMAND: /2/,/4/ {
COMMAND: }
END-OF-CYCLE:
INPUT: 'STDIN' line 2
PATTERN: 2
COMMAND: /2/,/4/ {
COMMAND: /2/! {
COMMAND: }
COMMAND: }
END-OF-CYCLE:
INPUT: 'STDIN' line 3
PATTERN: 3
COMMAND: /2/,/4/ {
COMMAND: /2/! {
COMMAND: /4/ d
COMMAND: p
3
COMMAND: }
COMMAND: }
END-OF-CYCLE:
INPUT: 'STDIN' line 4
PATTERN: 4
COMMAND: /2/,/4/ {
COMMAND: /2/! {
COMMAND: /4/ d
END-OF-CYCLE:
INPUT: 'STDIN' line 5
PATTERN: 5
COMMAND: /2/,/4/ {
COMMAND: }
END-OF-CYCLE:
通过管道连接一些grep命令就可以实现它(它在POSIX shell和BusyBox下运行):
cat my-file | grep -A1 my-regexp | grep -v -- '--' | grep -v my-regexp
-v
选项会显示不匹配的行