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



我的文件是这样的:

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

我想做的是写一个脚本,取代foo = bar中的bar,但只有当它属于FooBarB。因此,在上面的示例中,所有foo = bar行中只有第二个bar应该被替换。

我玩过sed,但我就是不能把它做对。我也想避免安装任何不一定预先安装在系统上的工具(我在Mac OS上),因为脚本也会被其他团队成员使用。

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

<编辑id> 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]替换(在匹配的行范围内)任何与1new-value匹配的正则表达式(foo[[:space:]]*=[[:space:]]*).+,其中1引用搜索正则表达式中的第一个捕获组。搜索正则表达式查找foo,后面跟着可选的空格,后面跟着=符号,然后再跟着空格,然后是其他任何东西,在您的情况下是旧值。

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

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

这可能适合您(GNU sed):

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

匹配字符串FooBarB并开始循环。

取下一行并研究它

如果行为空,则该节结束,因此跳出循环。

如果该行不包含字符串foo = bar,则取出下一行并继续循环。

否则,将新值替换为bar并完成循环。


可选(可能适用于macos用户?):

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

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

sed '/FooBar/h;G;/FooBarB/s/foo = bar/foo = baz/;P;d' file

在任何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

作为参考,GNU awk变体:

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

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

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

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

相关内容

  • 没有找到相关文章

最新更新