Командная строка Linux глобальный поиск и замена


Я пытаюсь найти и заменить строку во всех файлах, сопоставленных grep на машине linux. У меня есть некоторые части того, что я хочу сделать, но я не уверен, как лучше всего связать их вместе.

grep -n 'foo' * выдаст мне вывод в виде:

[filename]:[line number]:[text]

для каждого файла, возвращенного grep, я хотел бы заменить "foo" на " bar " и записать результат обратно в файл. Есть ли хороший способ сделать это? Может быть, причудливый трубопровод?

8 78

8 ответов:

вы имеете в виду поиск и замену строки во всех файлах, сопоставленных grep?

perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`

Edit

поскольку это, кажется, довольно популярный вопрос, я бы обновил.

в настоящее время я в основном использую ack-grep как это более удобно для пользователя. Таким образом, приведенная выше команда будет:

perl -p -i -e 's/old/new/g' `ack -l searchpattern`

для обработки пробелов в именах файлов вы можете запустить:

ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

вы можете сделать больше с ack-grep. Допустим, вы хотите ограничить поиск Только HTML файлы:

ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

и если пробел не является проблемой, это еще короче:

perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files

это, кажется, то, что вы хотите, основываясь на примере, который вы привели:

sed -i 's/foo/bar/g' *

он не является рекурсивным (он не будет опускаться в подкаталоги). Для хорошей замены решения в выбранных файлах по всему дереву я бы использовал find:

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

The *.html - это выражение, которому должны соответствовать файлы,.bak после -i делает копию исходного файла, с a .расширение bak (это может быть любое расширение, которое вам нравится) и g в конце САС выражение говорит sed заменить несколько копий в одной строке (а не только первый). Элемент -print найти-это удобство, чтобы показать, какие файлы были подобраны. Все это зависит от точных версий этих инструментов в вашей системе.

если sed(1) есть -i вариант, а затем использовать его следующим образом:

for i in *; do
  sed -i 's/foo/bar/' $i
done

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

ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
perl -pi.bak -e 's/foo/bar/' *

Мне нравится и используется вышеуказанное решение или общесистемный поиск и замена среди тысяч файлов:

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;

Я предполагаю, что с '*.htm?- вместо этого .HTML он ищет и находит .хтм И.html файлы похожи.

Я заменить .бак с более широкой системой используется Тильда ( ~ ), чтобы сделать очистку резервных файлов проще.

это работает с использованием grep без необходимости использовать perl или найти.

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

find . -type f -print0 | xargs -0 <sed/perl/ruby cmd> будет обрабатывать несколько имен файлов, содержащихся в пространстве, одновременно загружая один интерпретатор на пакет. Гораздо быстрее.

ответ уже дан при помощи найти и sed

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

вероятно, стандартный ответ. Или вы могли бы использовать perl -pi -e s/foo/bar/g' вместо .

для наиболее быстрого использования, вы можете найти команду rpl легче запомнить. Вот замена (foo -> bar), рекурсивно на все файлы в текущем каталоге:

rpl -R foo bar .

по умолчанию он недоступен большинство дистрибутивов Linux, но быстро установить (apt-get install rpl или подобные).

однако, для более жестких заданий, которые включают регулярные выражения и обратную замену, или переименования файлов, а также поиск и замену, самый общий и мощный инструмент, о котором я знаю,-это repren, небольшой скрипт Python, который я написал некоторое время назад для некоторых более сложных задач переименования и рефакторинга. Причины, по которым вы можете предпочесть это:

  • поддержка переименования файлов, а также поиск и замена содержимого файлов (включая перемещение файлов между каталогами и создание новых родительских каталогов).
  • см. изменения перед выполнением поиска и замены.
  • поддержка регулярных выражений с обратной подстановкой, целыми словами, без учета регистра и сохранением регистра (заменить foo - > bar, Foo -> Bar, FOO -> BAR) режимы.
  • работает с несколькими заменами, включая свопы (foo - > bar и bar - > foo) или наборы не уникальных замены (foo - > bar, f -> x).

Проверьте README для примера.

Это на самом деле проще, чем кажется.

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grep рекурсирует через ваше дерево (- R) и печатает только имя файла (-l), начиная с текущего каталога (./)
  • который передается в xargs, который обрабатывает их по одному (- n 1) и использует % в качестве заполнителя (-I%) в команде оболочки (sh-c)
  • в команде оболочки сначала печатается имя файла (ls %;)
  • затем sed выполняет встроенную операцию (- i), подстанцию ('s/') foo with bar (foo/bar), глобально (/g) в файле (опять же, представлен %)

легкий и непринужденный. Если вы хорошо разбираетесь в find, grep, xargs, sed и awk, почти нет ничего невозможного, когда речь заходит о манипуляциях с текстовыми файлами в bash:)