替换匹配模式中的第 n 行

替换匹配模式中的第 n 行

我有以下文本文件。

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
$

找到那条线,走两行,change, write,然后 quit。


@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

相关内容