Як виконати заміну sed-place, яка створює лише резервні копії файлів, які були змінені?


13

Я запустив наступне, щоб замінити термін, який використовується у всіх файлах у поточній робочій директорії:

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

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

Як виконати заміну, не створюючи всі ці непотрібні резервні копії?


мені здається, як помилка sed див
Hotschke

Можливо, є кращий підхід, використовуючи 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'

1
Я думаю, що тобі потрібно '{}'лише перед \;цим -exec.
Джандер

8

Find має -execоператора, який виконує довільну команду. Ще краще -exec- тест , тож ви можете зв'язати кілька -execс разом, і якщо попередні команди не вдасться, пізніші не виконуються. Рядок {}замінюється поточним іменем файлу і ;позначає кінець команди. Ви повинні цитувати обидва, щоб уникнути втручання оболонки.

Так:

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

У мене ніколи не було проблем з голими {}.
амфетамахін

1
@amphetamachine: Ні в мене немає, але сторінка man пропонує це. Це може бути csh річ, або можуть бути оболонки (не Bash і не POSIX), які розширюються {}на нульову рядок під час розширення дужок.
Джандер

4

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

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

-rповторювати назву
-lфайлу друку лише
-Zкінцевими іменами з null


-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

Ви забули .як перший аргумент find. Крім того, як мене не так давно поінформували, вам насправді не потрібно цитувати або уникати{}
Кевін

Не створюйте список файлів і не виконайте підстановку команд на цьому. Спочатку findстворюється список файлів, розділених новою лінією, потім оболонка розбиває цей список на будь-який пробіл (не лише нові рядки), а потім оболонка розглядає кожне слово як глобальний зразок. Це працює лише в тому випадку, якщо назви ваших файлів не містять пробілів або \[?*. Інші відповіді на цій сторінці показують, як це зробити правильно.
Жил 'SO- перестань бути злим'

@Kevin Це .непотрібно при знаходженні (принаймні, знайдено в Linux), оскільки воно передбачається, коли аргумент не проходить шлях.
в'язниця
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.