Как выполнить подстановку sed на месте, которая создает только резервные копии файлов, которые были изменены?

13

Я запустил следующее, чтобы заменить термин, используемый во всех файлах в текущем рабочем каталоге:

$ find . -type f -print0 | xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

Это выполняло подстановку слова, но также создавало .bupфайлы файлов, которые никогда не имели Ms. Johnsonстроки.

Как выполнить замену, не создавая все эти ненужные резервные копии?

Бельмин Фернандес
источник
мне кажется как баг
сэд
Вероятно, есть лучший подход, возможный с использованием exи условно (только если файл изменен), выполняемый :!cp '%' '%.bup'перед сохранением и выходом. Может быть стоит посмотреть.
Wildcard
Я написал вопрос о моей идее выше на viстеке обмена.
Wildcard

Ответы:

6

Вы можете проверить содержимое файлов, чтобы убедиться, что подстановка будет иметь место при работе sedс ними:

find . \
    -type f \
    -exec grep -q 'Ms. Johnson' {} \; \
    -print0 |
xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

Если вы хотите быть действительно умным, вы можете findвообще отказаться от использования :

grep -Z -l -r 'Ms. Johnson' |
    xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'
amphetamachine
источник
1
Я думаю, тебе нужно как '{}'раз перед тем, как ты \;в этом -exec.
Jander
8

В Find есть -execоператор, который выполняет произвольную команду. Более того, -execэто тест , так что вы можете -execобъединить несколько s вместе, и если более ранние команды не пройдут, более поздние не будут выполнены. Строка {}заменяется текущим именем файла и ;отмечает конец команды. Вы должны процитировать оба, чтобы избежать вмешательства оболочки.

Так:

find . -type f \
    -exec grep -q 'Ms. Johnson' '{}' \; \
    -exec sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g' '{}' \;
Jander
источник
У меня никогда не было проблем с использованием голых {}.
амфетамина
1
@amphetamachine: Я тоже, но страница руководства предлагает это. Это может быть что-то вроде csh, или могут быть оболочки (не Bash и не POSIX), которые раскрываются {}в пустую строку при выполнении фигурных скобок.
Декабрь
4

Я посмотрел на справочную страницу и не нашел способа сделать это напрямую sed, как я уверен, что вы сделали, прежде чем спрашивать. Я вижу несколько способов обойти это с помощью grep, но я думаю, что самый простой это:

grep -rlZ "Ms. Johnson" . | xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

-rrecurse
-lprint filename только
-Zконечные имена с нулем

Kevin
источник
-1

Похоже, вам нужно сначала создать список файлов, которые соответствуют вашим условиям поиска.

search="test"
for match in $(find -type f -exec grep -l $search "{}" \;)
do sed -i'.bup' -e "/$search/ s/$search/Mrs. Melbin/g" "$match"
done
laebshade
источник
Вы забыли .как первый аргумент find. Кроме того, как мне сообщили не так давно, на самом деле вам не нужно цитировать или избегать{}
Кевин
Не генерируйте список файлов и не выполняйте подстановку команд. Сначала findгенерируется список файлов, разделенных новой строкой, затем оболочка разбивает этот список на любые пробелы (не только на новые строки), а затем оболочка обрабатывает каждое слово как шаблон глобуса. Это работает, только если ваши имена файлов не содержат пробелов или \[?*. Другие ответы на этой странице показывают, как это сделать правильно.
Жиль "ТАК - перестань быть злым"
@Kevin В .find нет необходимости (по крайней мере, find найдено в linux), потому что он предполагается, когда аргумент не передан пути.
laebshade