Глобальный поиск и замена командной строки Linux
Я пытаюсь найти и заменить строку во всех файлах, сопоставленных grep на машине linux. У меня есть некоторые части того, что я хочу сделать, но я не уверен, как лучше всего связать их все вместе.
grep -n 'foo' *
даст мне вывод в виде:
[filename]:[line number]:[text]
Для каждого файла, возвращаемого grep, я хотел бы заменить "foo" на "bar" и записать результат обратно в файл. Есть ли хороший способ сделать это? Может быть, фантастический трубопровод?
Ответы
Ответ 1
Вы хотите найти и заменить строку во всех файлах, соответствующих 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
Ответ 2
Это похоже на то, что вы хотите, на основе приведенного вами примера:
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
для поиска - это удобство, чтобы показать, какие файлы были сопоставлены. Все это зависит от точных версий этих инструментов в вашей системе.
Ответ 3
Если ваш 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/' *
Ответ 4
Мне нравится и использовал вышеупомянутое решение или поиск в системе и замену среди тысяч файлов:
find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;
Я предполагаю, что с '*.htm?' вместо .html он ищет и находит файлы .htm и .html.
Я заменяю .bak более используемой системой тильдой (~), чтобы упростить очистку файлов резервных копий.
Ответ 5
Это работает с использованием grep без использования perl или find.
grep -rli 'old-word' * | xargs [email protected] sed -i 's/old-word/new-word/g' @
Ответ 6
find . -type f -print0 | xargs -0 <sed/perl/ruby cmd>
будет обрабатывать несколько пробелов, содержащих имена файлов, сразу загружая один интерпретатор за пакет. Гораздо быстрее.
Ответ 7
Ответ, уже использованный при использовании 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 script Я написал некоторое время назад для некоторых более сложных задач переименования и рефакторинга. Причины, по которым вы, возможно, предпочтете это:
- Поддержка переименования файлов, а также поиск и замена содержимого файла (включая перемещение файлов между каталогами и создание новых родительских каталогов).
- Перед выполнением поиска и замены ознакомьтесь с изменениями.
- Поддерживать регулярные выражения с обратной подстановкой, целыми словами, нечувствительностью к регистру и сохранением регистров (замените foo → bar, Foo → Bar, FOO → BAR).
- Работает с несколькими заменами, в том числе свопами (foo → bar и bar → foo) или наборами неповторимых замен (foo → bar, f → x).
Проверьте пример README.
Ответ 8
Это на самом деле проще, чем кажется.
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. Если вы хорошо разбираетесь в поиске, grep, xargs, sed и awk, почти ничего не получается, когда дело касается обработки текстовых файлов в bash:)