直接修改现有文件,仅对于包含“baz”的行用“bar”替换“foo”

直接修改现有文件,仅对于包含“baz”的行用“bar”替换“foo”

我有一个 food.txt 文件,如下所示:-

mangoes|foo|faa  
oranges|foo|faa  
chocolates|foo|baz  

我正在尝试更换酒吧如果满足条件baz。 (想在这里使用正则表达式 b*z )
当前使用下面的 sed 命令,但这不会直接修改现有文件。我也无法使用正则表达式 (b*z)。

sed '/baz/s/foo/bar/g' food.txt

请建议除“sed”之外的其他方法来直接修改现有文件。

PS:我尝试过sed -i,但我想使用 sed 以外的其他命令。
我正在使用 mobaxterm(操作系统 - Windows)

答案1

sed我认为在这种情况下使用没有什么问题。这是适合这项工作的工具。

您的命令运行良好(在给定的数据上):

$ sed '/baz/s/foo/bar/g' food.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

使用正则表达式匹配以行尾(而不是行上任何位置)开头|b和结尾的任何字符串:zbaz

$ sed '/|b.*z$/s/foo/bar/g' food.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

要更改文件(使用 GNU sed):

$ sed -i '/|b.*z$/s/foo/bar/g' food.txt

或(任何sed实施):

$ sed '/|b.*z$/s/foo/bar/g' food.txt >tmpfile && mv tmpfile food.txt

您还可以使用awk

$ awk 'BEGIN { OFS=FS="|" } $3 == "baz" { $2 = "bar" }; 1' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

^b.*z$或在第三个字段中匹配,

$ awk 'BEGIN { OFS=FS="|" } $3 ~ /^b.*z$/ { $2 = "bar" }; 1' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

... 或者磨坊主( mlr),此处将输入读取为用作字段分隔符的无标头 CSV 文件|

$ mlr --csv -N --fs pipe put '$3 == "baz" { $2 = "bar" }' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

或者,

$ mlr --csv -N --fs pipe put '$3 =~ "^b.*z$" { $2 = "bar" }' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

使用 或 Miller 的好处awk是可以更轻松、更安全地将模式与孤立字段进行匹配。 Miller 的另一个好处是了解 CSV 引用规则。

答案2

awk与以下一起使用gsub()

awk '/baz$/ {gsub("foo", "bar")};1' food.txt
  • /baz$/如果需要,可以使用任何正则表达式模式来匹配而不是,

  • 如果模式匹配,则gsub()替换所需的字符串


对于内部编辑,最新版本的 GNU awk(>=4.1.0) 具有就地修改选项:

awk -i inplace '/baz$/ {gsub("foo", "bar")};1' food.txt

否则,您可以使用spongeGNUmoreutils或使用临时文件:

awk '/baz$/ {gsub("foo", "bar")};1' food.txt >temp_food.txt && \
      mv temp_food.txt food.txt

例子:

$ cat file.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|foo|baz

$ awk '/baz$/ {gsub("foo", "bar")};1' file.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

答案3

虽然我不知道为什么你不想使用sed -iwhich 完全满足你的需要,但另一种选择是perl

$ perl -pe 's/foo/bar/g if /b*z/' food.txt 
mangoes|foo|faa  
oranges|foo|faa  
chocolates|bar|baz

-pe意味着“在应用脚本后打印输入文件的每一行。

您可以使用-i就地编辑文件:

perl -i -pe 's/foo/bar/g if /b*z/' food.txt 

另请注意,正则表达式的b*z意思是“匹配 0 个或多个b后跟 a z。它在这里可以工作,因为通过忽略andb*z进行匹配,并且仅匹配。换句话说,它将匹配 any,因为 any将是 0后跟 a的示例。我认为您可能想要使用的是 b.*z (ab 后跟 0 个或多个字符,然后是 az):barbazzzbz

perl -i -pe 's/foo/bar/g if /b.*z/' food.txt 

答案4

自动文本编辑的实际最佳工具是ex

虽然应该可以ex直接调用,但是我发现在 MobaXterm 中我必须打电话vim -e

因此,进行自动编辑的最佳方法在 MobaXterm 中(也适用于其他 *nix 系统)是:

printf '%s\n' 'g/b.*z/s/foo/bar/g' x | vim -es food.txt

要完全兼容 POSIX,只需将其更改为:

printf '%s\n' 'g/b.*z/s/foo/bar/g' x | ex -s food.txt

但是,调用该ex命令可能无法在 MobaXterm 上正常工作(在我的安装上不起作用)。如果该版本失败,请尝试vim -es该命令的版本。ex

相关内容