我有一个 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
和结尾的任何字符串:z
baz
$ 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
否则,您可以使用sponge
GNUmoreutils
或使用临时文件:
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 -i
which 完全满足你的需要,但另一种选择是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):bar
b
a
z
z
z
b
z
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