这种感觉在 中会更容易awk
,但我很好奇是否sed
可以做到。这是我的输入:
line 1
line 2
line 3
line 1
line 2
line 3
line 1
line 2
line 3
我想编写一个就地正则表达式来查找第二个line 1
,然后替换line 3
之后找到的所有内容。输出如下所示:
line 1
line 2
line 3
line 1
line 2
replaced
line 1
line 2
replaced
我并不是真的在寻找仅适用于此输入的“聪明”解决方案。我想了解是否有通用的方法来搜索和替换后与 的比赛sed
。
我以为解决方案就在某个地方文档addr
,但它似乎没有描述/starting point/,s/...
为您可以做的事情,并且当我尝试这样做时出现错误。
答案1
为此,您需要计算已经看到目标线的次数。不幸的是,把任何东西算进去sed
都是 PITA。你最好在awk
or中这样做perl
。例如:
$ perl -p -e '$found++ if m/line 1/;
next if $found < 2;
s/line 3/replaced/' input.txt
line 1
line 2
line 3
line 1
line 2
replaced
line 1
line 2
replaced
或在awk
:
$ awk '/line 1/ { found++ };
found < 2 { print ; next };
{ sub(/line 3/,"replaced") ; print }' input.txt
line 1
line 2
line 3
line 1
line 2
replaced
line 1
line 2
replaced
这些方法有效,但远非最佳解决方案,尤其是awk
版本(只是 perl 的直接翻译)。
顺便说一句,让任何一个版本都使用正则表达式来搜索和数数,要搜索的正则表达式和代替、替换字符串,甚至所需的计数都作为命令行选项,而不是在脚本中对它们进行硬编码。
答案2
使用 GNU sed
(对于 0 地址1):
$ sed '0,/^line 1$/b; /^line 1$/,$ s/^line 3$/replaced/' < file
line 1
line 2
line 3
line 1
line 2
replaced
line 1
line 2
replaced
我们b
牧场出去对于第一个0,/^line 1/
范围,这意味着第二个范围/^line 1$/,$
仅在到达第二次出现 时才看到其起始行line 1
。
使用awk
,可以更轻松地发现 的第 n次出现line 1
:
awk '$0 == "line 1" && ++n == 2, eof {if ($0 == "line 3") $0 = "replaced"}; 1'
(这里eof
只是任何其他未初始化的变量,因此会产生错误的来表示第一的,最后的范围没有尽头;与0
字面意思相同,但更易读;类似地,我们可以使用!skip
而不是1
来表示真的对于要打印的当前记录,但使用1
是足够惯用的,它应该被所有用户识别awk
,并且更清晰的替代方案是{print}
)。
1 并且由于我们假设 GNU sed
,我们也可以在 1;more commands
之后添加b
,这也不是标准的。我们需要另一个-e 'more commands'
和其他sed
的。
答案3
在缓冲区(模式空间)中创建一个计数器并在每一行上检查它:
sed '
/^line 1/{x;/11/!s/^/1/;x}
x;/11/{x;b1};x;b
:1;s/^line 3$/replaced/
' file
我们用作1
计数器单位
[更新] 更好的性能:
sed '
/^line 1$/{x;/11/!s/^/1/;x}
/^line 3$/!b
x;/11/!{x;b}
x;s/.*/replaced/
' file
仅在与模式匹配的行中检查计数器 -/^line 3$/
答案4
使用 GNU sed,我们可以通过将计数存储为.
保存空间中的点来执行任务。
sed -e '
/^line 1$/,/^line 1$/{//!b
x;s/^$/./;x;t
:a;n;s/^line 3$/REPLACED/;ba
}
' file
另一种可能性是不使用范围运算符并使用 GNU sed 中的扩展正则表达式模式。这里换行符\n
作为计数器,存储在保留空间中。
sed -Ee '
/^line 1$/H
x;s/^(\n{1,2}).*/\1/;x
/^line 3$/G
/\n.{2}/c REPLACED
s/\n//g
' file
Perl 可以用类似的方式做同样的事情
perl -lpe '
s/^line 3$/$a?"REPLACED":$&/e
unless
m?^line 1$? ... /^line 1$(?{$a++})/;
' file
使用 awk,我们可以很好地将正则表达式与变量结合起来并递增一
awk '
1 < ( k += /^line 1$/ ) &&
sub(/^line 3$/,"REPLACED") ||
1' file