Как сообщить об изменениях на месте «sed»

23

При использовании sedдля замены строк на месте, есть ли способ заставить его сообщать об изменениях, которые он делает (не полагаясь на различие старых и новых файлов)?

Например, как я могу изменить командную строку

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

чтобы я мог видеть изменения, которые сделаны на лету?

ricab
источник

Ответы:

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 first >>> сначала бар
третье: foo >>> третье: бар
don_crissti
источник
16

Вы можете сделать это за два прохода, используя действие print на первом проходе с:

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

где --quietзаставляет sed не показывать каждую строку, а pсуффикс показывает только те строки, где подстановка соответствует.

Это имеет ограничение, что sed не будет показывать, какие файлы изменяются, что, конечно, может быть исправлено с некоторой дополнительной сложностью.

MSW
источник
Это не совсем то, что я хотел, так как вам нужно два прохода, но я голосую, потому что я только что узнал эту вещь, и это очень полезно. Особенно, если, как вы сказали, файлы также были распечатаны. Нечто подобноеfor x in `find . -type f`; do echo ///File $x: ; sed --quiet 's/abc/def/gp' $x; done
Рикаб
У меня много sedвыражений, я не хочу писать /gpдля каждого. Как установить это глобально?
Алхелал
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 это нормально, вы не должны принимать ответ, если он на самом деле не отвечает на ваш вопрос. Имейте в виду, что для таких простых вещей perlсинтаксис очень похож на синтаксис, sedи я не думаю, что то, о чем вы просите, действительно возможно sed.
Тердон
3

Это возможно с помощью флага w, который записывает текущий файл в файл. Таким образом, добавив его в команду замены, мы можем сообщить о последовательных заменах в файл и распечатать его после завершения работы. Мне также нравится раскрашивать замененную строку с помощью grep.

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

Обратите внимание, что между w и именем файла должен быть только один пробел .

Это даже лучше, чем diff, поскольку diffing также может показывать изменения, даже если они не были сделаны sed.

Джек
источник
Просто проверил это, и sed только пишет строки, которым он заменен sed.done. Поэтому строки с «Кому» в исходном файле не печатаются sed.done, поэтому, когда grep "To" sed.doneвы увидите только те строки, которые были изменены sed. Вы не увидите исходную строку в файле, прежде чем она будет заменена, если вы к этому
стремитесь
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
andy987
источник