如果前一行与另一个文本匹配,则替换文件中的文本

时间:2021-07-23 07:04:27

标签: macos shell awk sed

我的文件如下所示:

FooBarA
foo bar
foo = bar
FooBarB
foo bar
foo = bar
FooBarC
foo bar
foo = bar
...

我想做的是编写一个脚本来替换 bar 中的 foo = bar,但前提是它属于 FooBarB。因此,在上面的示例中,仅应替换所有 bar 行中的第二个 foo = bar

我已经尝试过 sed,但我就是无法正确完成。我还想避免安装任何不一定预装在系统上的工具(我使用的是 Mac OS),因为其他团队成员也会使用该脚本。

4 个答案:

答案 0 :(得分:2)

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

Priority 2

匹配字符串 sed '/FooBarB/{:a;n;/^$/b;/foo = bar/!ba;s//foo = baz/}' file 并开始循环。

获取下一行并研究它。

如果该行为空,则该节已完成,因此跳出循环。

如果该行不包含字符串 FooBarB,则获取下一行并继续循环。

否则,用新值替换 foo = bar 并完成循环。


替代方案(可能适用于 macos 用户?):

bar

由于 OP 将输入数据更改为问题的另一个解决方案:

sed -e '/FooBarB/{:a' -e 'n;/^$/b;/foo = bar/!ba;s//foo = baz/;}' file

答案 1 :(得分:2)

使用 sed(使用 macOS 的 sed 和 GNU sed 进行测试)的一种方法是:

replace.sed
#!/usr/bin/env sed -Ef

/FooBarB/,/^FooBar/ {
  s/(foo[[:space:]]*=[[:space:]]*).+/\1new-value/
}

这是它的作用:

  1. /FooBarB/,/^FooBar/ 匹配一系列行,其中第一行匹配正则表达式 /FooBarB/,最后一行匹配正则表达式 /^FooBar/(这是下一个“组”)。两个正则表达式之间的逗号是 sed 中范围匹配的语法。

  2. s/(foo[[:space:]]*=[[:space:]]*).+/\1new-value/ — [s] 替换(在匹配的行范围内)与正则表达式 (foo[[:space:]]*=[[:space:]]*).+\1new-value 匹配的任何内容,其中 \1 引用第一个捕获在搜索正则表达式中分组。搜索正则表达式查找 foo 后跟可选空格,后跟一个 = 符号,再次是空格,然后是其他任何内容,在您的情况下是旧值。

您可以在一行中完成所有操作,但我想展示一个更易于理解的版本(就 sed 而言,无论如何):

sed -E '/FooBarA/,/^FooBar/s/(foo[[:space:]]*=[[:space:]]*).+/\1new-value/' temp.md 

答案 2 :(得分:2)

在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ awk -v tgt='FooBarB' -v val='whatever'  '
    NF==1{tag=$0} (NF>1) && (tag==tgt) && sub(/=.*/,"= "){$0=$0 val}
1' file
FooBarA
foo bar
foo = bar
FooBarB
foo bar
foo = whatever
FooBarC
foo bar
foo = bar

答案 3 :(得分:0)

作为参考,GNU awk 变体:

awk -v v="newvalue" 'BEGIN{FS=OFS="\n";RS=ORS="\n\n"}$1=="FooBarB"{$3="foo = " v}1' file

通过使用选项 -v,变量 v 保存所需的字符串。

BEGIN语句分别设置输入、输出字段分隔符,输入输出记录分隔符为一、二回车。
这样,记录由包含模式 Foobar[ABC] 的多行块组成。

最后一条语句通过重写第三行来设置新值。