У меня есть файл food.txt следующим образом:
mangoes|foo|faa oranges|foo|faa chocolates|foo|baz
Я пытаюсь заменить foo на bar, если условие baz выполнено. (хотел бы использовать здесь регулярное выражение b * z)
В настоящее время используется команда sed ниже, но это напрямую не изменяет существующий файл. Я также не могу использовать регулярное выражение (b * z).
sed '/baz/s/foo/bar/g' food.txt
Пожалуйста, предложите другой способ, кроме «sed», напрямую изменить существующий файл.
PS: Я попробовал sed -i
но я бы хотел использовать другую команду, кроме sed.
Я использую mobaxterm (ОС – Windows)
Фактический лучший инструмент для автоматических текстовых изменений – 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
версия завершилась с ошибкой.
В этом случае я не вижу ничего плохого в использовании 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
с gsub()
:
awk '/baz$/ {gsub("foo", "bar")};1' food.txt
Используйте любой шаблон Regex для соответствия вместо /baz$/
, если хотите
если шаблон совпадает, выполните gsub()
чтобы заменить нужные строки
Для редактирования inpace в последней версии GNU awk
(> = 4.1.0) есть опция модификации inplace:
awk -i inplace '/baz$/ {gsub("foo", "bar")};1' food.txt
В противном случае вы можете использовать sponge
из GNU moreutils
или использовать временный файл:
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
Хотя я понятия не имею, почему вы не хотите использовать sed -i
который делает именно то, что вам нужно, другой вариант будет 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
за которым следует z
. Он будет работать здесь, потому что b*z
соответствует bar
, игнорируя b
и a
и просто сопоставляя z
. Другими словами, он будет соответствовать любому z
так как любой z
будет примером 0 b
за которым следует z
. Я думаю, что вы, вероятно, хотите использовать, это b. * z (ab следует 0 или более символов, а затем az):
perl -i -pe 's/foo/bar/g if /b.*z/' food.txt