![替换匹配模式中的第 n 行](https://linux22.com/image/178389/%E6%9B%BF%E6%8D%A2%E5%8C%B9%E9%85%8D%E6%A8%A1%E5%BC%8F%E4%B8%AD%E7%9A%84%E7%AC%AC%20n%20%E8%A1%8C.png)
我有以下文本文件。
banana
apple
juice
mango
something
我正在搜索模式juice
,并且我想以相反的顺序找到该匹配模式的第二行(即匹配模式上方的两行)并将其替换为coconut
。
预期输出:
coconut
apple
juice
mango
something
我尝试了以下操作,但它只是删除了上面的两行,而不是我正在寻找的那一行。
tac foo.txt |sed '/juice/I,+2 d' |tac
mango
something
我认为调整上面的脚本可以完成这项工作,但我不确定。
注意:匹配不会重复出现,并且不需要是精确匹配(也就是说,也可以在长行中找到匹配)。匹配应区分大小写。
答案1
如果ed
可以的话,你需要编辑一个文件,而不是一个流,并且只有一个 juice
:
$ more <<-EOF | ed -s ./tmp.txt
/juice/
-2
c
coconut
.
w
q
EOF
$
找到那条线,走两行,c
hange,
w
rite,然后 q
uit。
@d-ben-knoble 在评论中建议的更紧凑的变体:
$ printf '%s\n' '/^juice$/-2s/.*/coconut/' w q | ed -s ./tmp.txt
答案2
按照你的做法,
tac file|sed '/juice/{n;n;s/.*/coconut/}'|tac
/juice/
与 匹配一行juice
。n;n;
打印当前行和下一行。s/.*/coconut/
进行替换。
显然你有GNU sed,所以你也可以使用-z
将整个文件放入内存并直接编辑上面的第二行juice,
sed -rz 's/[^\n]*(\n[^\n]*\n[^\n]*juice)/coconut\1/' file
[^\n]
表示“不是换行符”,括号捕获由反向引用()
复制的组。\1
答案3
$ tac file | awk 'c&&!(--c){$0="coconut"} /juice/{c=2} 1' | tac
coconut
apple
juice
mango
something
答案4
这是另一种awk
方法,这次是双遍方法:
awk 'NR==FNR&&/juice/{m=FNR} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file
- 我们指定文件两次作为命令行参数,以便它被处理两次。
- 在第一遍中(其中
FNR
,每个文件行计数器,等于NR
全局行计数器),我们只需识别搜索模式juice
出现在哪一行,并将其存储在变量 中m
。 - 在第二遍中,我们将行号设置
m-2
为替换文本coconut
。 - 作为一般规则,我们打印包含任何修改的行,但仅在第二遍中(其中条件
NR>FNR
评估为“true”)。
如果您有 GNU awk
(其他一些awk
实现也支持这一点),您可以使用以下命令在找到匹配项后立即中止第一遍,从而加快该过程nextfile
:
awk 'NR==FNR&&/juice/{m=FNR;nextfile} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file