Как найти и заменить

255

Мне нужно рекурсивно искать указанную строку во всех файлах и подкаталогах в каталоге и заменить эту строку другой строкой.

Я знаю, что команда найти его может выглядеть так:

grep 'string_to_find' -r ./*

Но как я могу заменить каждый экземпляр string_to_findдругой строкой?

billtian
источник
Я не верю, что grep может это сделать (я могу ошибаться). Проще было бы использовать sed или perl для замены
Memento Mori
2
Попробуйте использоватьsed -i 's/.*substring.*/replace/'
Eddy_Em
2
@Eddy_Em Это заменит всю строку с заменой. Вы должны использовать группирование, чтобы захватить часть строки до и после подстроки, а затем поместить ее в строку замены. sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl

Ответы:

250

Другой вариант - использовать find, а затем передать его через sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;
rezizter
источник
34
На Терминале OS X 10.10 требуется правильная строка расширения для параметра -i. Например, find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;Во всяком случае, предоставление пустой строки по-прежнему создает файл резервной копии в отличие от описанного в руководстве ...
Eonil
10
Почему я получаю «sed: RE error: недопустимая последовательность байтов». И да, я добавил -i ""для OS X. Это работает иначе.
тако
2
У меня возникла проблема с недопустимой последовательностью байтов в macOS 10.12, и этот вопрос / ответ решил мою проблему: stackoverflow.com/questions/19242275/… .
abeboparebop
3
Это касается каждого файла, поэтому время файла изменяется; и преобразует окончания строки из CRLFв LFWindows.
jww
183

Я получил ответ.

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'
billtian
источник
14
Это будет сканировать соответствующие файлы дважды ... один раз с, grepа затем снова с sed. Использование findметода более эффективно, но упомянутый метод работает.
cmevoli
41
На OS X вам нужно изменить , sed -i 's/str1/str2/g'чтобы sed -i "" 's/str1/str2/g'для этого к работе.
jdf
6
@cmevoli с помощью этого метода grepпросматривает все файлы и sedпросматривает только файлы, соответствующие которым grep. При использовании findметода из другого ответа findсначала перечисляются все файлы, а затем sedвыполняется сканирование всех файлов в этом каталоге. Таким образом, этот метод не обязательно медленнее, он зависит от количества совпадений и различий в скоростях поиска между sed, grepи find.
Joelostblom
4
Таким образом, OTOH позволяет ПРОСМОТРЕТЬ, что находит grep ПЕРЕД фактической заменой, значительно уменьшая риск неудачи, особенно для таких регулярных выражений, как я
Lennart Rolland
2
Это также полезно, когда ваша замена grep более умна, чем sed. Например, ripgrep подчиняется .gitignore, а sed - нет.
user31389
44

Вы могли бы даже сделать это так:

пример

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Это будет искать строку « windows » во всех файлах относительно текущего каталога и заменять « windows » на « linux » для каждого вхождения строки в каждом файле.

Дулит Де Коста
источник
2
Это grepполезно, только если есть файлы, которые не должны быть изменены. Запуск sedвсех файлов обновит дату изменения файла, но оставит содержимое без изменений, если совпадений нет.
tripleee
@tripleee: будьте осторожны с ... но [sed] оставьте содержимое без изменений, если совпадений нет " . -iЯ полагаю, что при использовании sedизменяется время файла каждого файла, к которому он прикасается, даже если содержимое остается неизменным. sedтакже преобразует окончания строк Я не использую sedна Windows в CRLFLF
репозитории
Эта команда нуждается в "" после -i, чтобы обозначить, что никакие файлы резервных копий не будут сделаны после замены на месте, по крайней мере, в macosx. Проверьте страницу руководства для деталей. Если вы хотите создать резервную копию, вы должны указать расширение создаваемого файла.
spinyBabbler
31

Это работает лучше всего для меня на OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Источник: http://www.praj.com.au/post/23691181208/grep-replace-text-string-in-files

Марк Джучли
источник
это потрясающе! также работает с ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi
@sebastiankeller В вашей команде Perl отсутствует последняя косая черта, что является синтаксической ошибкой.
tripleee
3
Почему sort -uчетная часть этого? При каких обстоятельствах вы ожидаете grep -rlполучить одно и то же имя файла дважды?
tripleee
5

Другие решения смешивают синтаксис регулярных выражений. Для того, чтобы использовать Perl / шаблоны PCRE для обоих поиска и замены, а только процесс согласования файлов, это работает очень хорошо:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

где match1и match2обычно идентичны, но match1могут быть упрощены, чтобы удалить более продвинутые функции, которые имеют отношение только к замене, например, захват групп.

Перевод: grepрекурсивно и список файлов, которые соответствуют этому шаблону PCRE, разделенные nul для защиты любых специальных символов в имени файла, затем передают те имена файлов, к xargsкоторым ожидает разделенный нулями список, но ничего не сделают, если не будут получены имена, и получить perlзамену строк, где найдены совпадения.

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

WALF
источник
Сам Perl вполне способен к рекурсии файловой структуры. На самом деле, есть инструмент, find2perlкоторый поставляется с Perl, который делает подобные вещи без каких-либо xargsхитростей.
tripleee
@tripleee findне ищет содержимое файла, и смысл состоит в том, чтобы обрабатывать только совпадающие файлы без написания Perl-программы.
Уолф
Это хорошее решение для Windows, поскольку оно позволяет избежать проблемы решений на основе sed, заключающихся в преобразовании концов строк. Спасибо!
JamHandy
4

Обычно не с grep, а с sed -i 's/string_to_find/another_string/g'или perl -i.bak -pe 's/string_to_find/another_string/g'.

minopret
источник
3

Будьте очень осторожны при использовании findи sedв git-репо! Если вы не исключите двоичные файлы, вы можете получить следующую ошибку:

error: bad index file sha1 signature 
fatal: index file corrupt

Чтобы устранить эту ошибку, вам нужно отменить sed, заменивnew_string на ваш old_string. Это вернет замененные строки, поэтому вы вернетесь к началу проблемы.

Правильный способ поиска и замены строки - вместо этого пропустить findи использовать grep, чтобы игнорировать двоичные файлы:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Кредиты для @hobs

tsveti_iko
источник
1

Вот что я бы сделал:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

он будет искать все файлы, содержащиеся filenameв имени файла под /path/to/dir, чем для каждого найденного файла, искать строку с searchstringи заменять oldнаnew .

Хотя, если вы хотите не искать конкретный файл со filenameстрокой в ​​имени файла, просто сделайте:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

Это будет делать то же самое выше, но со всеми файлами, найденными в /path/to/dir.

tinnick
источник
0

Другой вариант - просто использовать Perl с Globstar.

Включение shopt -s globstarв вашем .bashrc(или где бы то ни было) позволяет **шаблону glob рекурсивно сопоставлять все подкаталоги и файлы.

Таким образом, использование perl -pXe 's/SEARCH/REPLACE/g' -i **будет рекурсивно заменено SEARCHна REPLACE.

-XФлаг говорит Perl , чтобы «отключить все предупреждения» - это означает , что он не будет жаловаться на каталоги.

Globstar также позволяет делать такие вещи , как , sed -i 's/SEARCH/REPLACE/g' **/*.extесли вы хотите заменить SEARCHс REPLACEво всех дочерних файлах с расширением .ext.

GuiltyDolphin
источник
«Другой вариант - просто использовать perl с globstar ...» - не на машинах Posixy, таких как Solaris. Вот почему я специально ищу grepи sed.
jww