Linux командной строки глобального поиска и замены - PullRequest
78 голосов
/ 23 января 2009

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

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

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

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

Ответы [ 8 ]

108 голосов
/ 23 января 2009

Похоже, что вы хотите, основываясь на приведенном вами примере:

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

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

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

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

70 голосов
/ 23 января 2009

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

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

Редактировать

Так как этот вопрос кажется довольно популярным, подумал, что я обновлю.

В настоящее время я в основном использую 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
13 голосов
/ 23 января 2009

Если у вашего 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/' *
6 голосов
/ 20 июля 2010

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

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

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

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

4 голосов
/ 21 декабря 2013

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

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @
3 голосов
/ 01 декабря 2011

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

1 голос
/ 19 ноября 2015

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

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 с баром (foo / bar), глобально (/ g) над файлом (опять же, представленным%)

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

1 голос
/ 22 марта 2015

Уже дан ответ об использовании find и sed

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

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

Для наиболее быстрого использования команду 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 для примеров.

...