Як повідомити про зміни "sed" на місці


23

Чи використовується sedспосіб заміни рядків на місці, чи є спосіб змусити його звітувати про зміни, які він робить (не покладаючись на різні старі та нові файли)?

Наприклад, як я можу змінити командний рядок

find . -type f | xargs sed -i 's/abc/def/g'

тож я бачу зміни, які вносяться на льоту?

Відповіді:


15

Ви можете використовувати sed«s wпрапор або /dev/stderr, /dev/tty, /dev/fd/2якщо підтримується у вашій системі. Наприклад, із введенням fileтипу:

foo first
second: missing
third: foo
none here

біг

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

Виходи:

bar first
third: bar

хоча fileвміст було змінено на:

bar first
second: missing
third: bar
none here

Тож у вашому випадку працює:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

буде редагувати файли на місці та виводити:

./file1:
барні речі
більше бар

./file2:

./file3:
бар перший
третій: бар

Ви також можете надрукувати щось на зразок original line >>> modified line:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

редагує файли на місці та виводить:

./file1:
foo речі >>> бар речі
більше foo >>> більше бар

./file2:

./file3:
foo перший >>> перший бар
третій: foo >>> третій: bar

16

Ви можете зробити це за два проходи, використовуючи дію print під час першого проходу за допомогою:

find . -type f | xargs sed --quiet 's/abc/def/gp'

де --quietsed не показує кожен рядок, а pсуфікс показує лише рядки, де підміна відповідає.

Це має обмеження, що sed не покаже, які файли змінюються, які, звичайно, можна було б виправити з деякою додатковою складністю.


Це не зовсім те, що я хотів, тому що вам потрібні два переходи, але я голосую, тому що я тільки що дізнався цю річ, і це дуже корисно. Особливо, якщо, як ви сказали, файли також були надруковані. Щось на кшталтfor x in `find . -type f`; do echo ///File $x: ; sed --quiet 's/abc/def/gp' $x; done
ricab

@msw У мене багато sedвиразів, я не хочу писати /gpдля кожного. Як це встановити глобально?
alhelal

8

Я не думаю, що це можливо, але можливо вирішити замість цього використання perl:

find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 

Це надрукує змінені рядки до стандартної помилки. Наприклад:

$ cat foo
fooabcbar
$ find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

Ви також можете зробити це трохи складніше, надрукувавши номер рядка, ім'я файлу, оригінальну лінію та змінену лінію:

$ find . -type f | 
   xargs perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar

+1 Ви також можете використовувати $ARGVім'я файлу, яким керується.
Джозеф Р.

@JosephR. хороший момент, додав.
terdon

Дякую, це, мабуть, корисно (не перевіряв сам). Я не вибираю, оскільки він не використовує sed, тому він точно не відповідає на питання.
ricab

@ricab це нормально, ви не повинні приймати відповідь, якщо вона насправді не відповідає на ваше запитання. Майте на увазі, що для таких простих речей perlсинтаксис дуже схожий на sed's, і я не думаю, що те, що ви просите, насправді можливо sed.
terdon

3

Можна використовувати прапор w, який записує поточний шаблон у файл. Таким чином, додавши його до команди-замінника, ми можемо повідомити про послідовні заміни у файл та надрукувати його після завершення завдання. Мені також подобається розфарбувати замінений рядок grep.

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

Зауважте, що між w та його ім'ям має бути лише один пробіл .

Це навіть краще, ніж розрізнення, оскільки дифференціювання може також показати зміни, навіть якщо вони не були зроблені sed.


Тільки тестував його, і sed пише лише рядки, які він замінив sed.done. Рядки з "До" в початковому файлі не надруковані в sed.doneтакий спосіб, коли grep "To" sed.doneви побачите лише рядки, на які змінено sed. Ви не побачите оригінальний рядок у файлі до його заміни, якщо це те, на що ви прагнете ...
aairey

1

Мені подобається рішення @terdon - perl добре для цього.

Ось моя перероблена версія, яка:

  • не намагатиметься змінювати файли, у яких немає відповідного рядка
  • створить резервну копію попередньої версії файлів, які змінюються (створити .bak версію)
  • перелічить кожен змінений файл / білизну та покаже СТАРУ та НОВУ версії цього рядка з відступом та під ним для легкого читання

код

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

Приклад виведення

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.