Как рекурсивно найти и заменить все вхождения строки в дереве каталогов?

Используя только grep и sed, как мне заменить все вхождения:

a.example.com

с участием

b.example.com

в текстовом файле в дереве каталогов /home/user/, рекурсивно находя и заменяя все вхождения во всех файлах в подкаталогах.


person Tony    schedule 18.10.2009    source источник


Ответы (9)


Попробуй это:

find /home/user/ -type f | xargs sed -i  's/a\.example\.com/b.example.com/g'

Если вы хотите игнорировать точечные каталоги

find . \( ! -regex '.*/\..*' \) -type f | xargs sed -i 's/a\.example\.com/b.example.com/g'

Изменить: экранированные точки в поисковом выражении

person vehomzzz    schedule 18.10.2009
comment
Заменит ли это строку в каждом файле в каталоге / home / user? Включая подкаталоги? - person Tony; 18.10.2009
comment
Да, будет, хотя вы должны указать какой-то шаблон -name 'pattern' после find - person vehomzzz; 18.10.2009
comment
что ты имеешь в виду какой-то узор? - person Tony; 18.10.2009
comment
Шаблон вроде -name 'pattern' - где шаблон может быть похож на список доменов * - что-то, что сужает поиск. - person Dennis Williamson; 18.10.2009
comment
Согласно stackoverflow.com/a/1583282/477451, рекомендуется использовать -print0 и -0 в find и xargs команды соответственно. - person Mansoor Siddiqui; 30.07.2013
comment
Я бы посоветовал обновить ответ приведенными выше предложениями. Надеюсь, это приведет к принятию этого ответа. - person TSG; 15.01.2018
comment
в моем случае он выполнял поиск и замену во всей системе, даже если я ввел каталог. Почему? Не так уж и плохо, но в некоторых других случаях может быть не так хорошо ... - person Pathros; 20.03.2018

Попробуй это:

grep -rl 'SearchString' ./ | xargs sed -i 's/REPLACESTRING/WITHTHIS/g'

grep -rl будет рекурсивно искать SEARCHSTRING в каталогах ./ и заменять строки с помощью sed.

Ex:

Замена имени TOM на JERRY с использованием строки поиска как SWATKATS в каталоге CARTOONNETWORK

grep -rl 'SWATKATS' CARTOONNETWORK/ | xargs sed -i 's/TOM/JERRY/g'

Это заменит TOM на JERRY во всех файлах и подкаталогах в CARTOONNETWORK, где бы ни находилась строка SWATKATS.

person Abhinav Deshpande    schedule 30.06.2016
comment
Спасибо за такой ответ, а как записать в него другие файлы, чтобы не наделать ошибок? - person Revolucion for Monica; 10.02.2018

Я знаю, что это действительно старый вопрос, но ...

  1. Ответ @vehomzzz использует find и xargs, когда в вопросах явно указано только grep и sed.

  2. @EmployedRussian и @BrooksMoses пытались сказать, что это дублирование awk и sed, но это не так - опять же, вопрос явно говорит grep и sed только.

Итак, вот мое решение, если вы используете Bash в качестве оболочки:

OLDIFS=$IFS
IFS=$'\n'
for f in `grep -rl a.example.com .` # Use -irl instead of -rl for case insensitive search
do
    sed -i 's/a\.example\.com/b.example.com/g' $f # Use /gi instead of /g for case insensitive search
done
IFS=$OLDIFS

Если вы используете другую оболочку, такую ​​как Unix SHell, дайте мне знать, и я постараюсь найти корректировку синтаксиса.

P.S .: Вот однострочный текст:

OLDIFS=$IFS;IFS=$'\n';for f in `grep -rl a.example.com .`;do sed -i 's/a\.example\.com/b.example.com/g' $f;done;IFS=$OLDIFS

Источники:

person Timberwolf    schedule 15.09.2015
comment
Sed поддерживает использование альтернативного разделителя символов, такого как %, который не является обычным в URL-адресах, поэтому вам не нужно экранировать все косые черты. - person dragon788; 02.04.2018
comment
@ dragon788 Я не знал этого, но я не избегал косых черт. Я избегал менструаций. Точка - это специальный символ регулярного выражения для любого символа, поэтому его нужно экранировать, чтобы точно соответствовать точке в доменном имени. - person Timberwolf; 04.04.2018

У меня работает следующая команда:

find /path/to/dir -name "file.txt" | xargs sed -i 's/string_to_replace/new_string/g'

если строка содержит косую черту «путь / к / каталогу», ее можно заменить другим символом для разделения, например «@» вместо «/».

Например: 's@string/to/replace@new/string@g'

person Yahor M    schedule 19.03.2018

На macOS у меня ни один из ответов не помог. Я обнаружил, что это произошло из-за различий в том, как sed работает в macOS и других системах BSD по сравнению с GNU.

В частности, BSD sed принимает параметр -i, но требует суффикс для резервной копии (но допускается пустой суффикс)

grep версия из этого ответа.

grep -rl 'foo' ./ | LC_ALL=C xargs sed -i '' 's/foo/bar/g'

find версия из этого ответа.

find . \( ! -regex '.*/\..*' \) -type f | LC_ALL=C xargs sed -i '' 's/foo/bar/g'

Не пропускайте Regex, чтобы игнорировать . папки, если вы используете репозиторий Git. Я понял это на собственном горьком опыте!

Этот LC_ALL=C параметр предназначен для того, чтобы избежать получения sed: RE error: illegal byte sequence, если sed находит последовательность байтов, которая не является допустимым символом UTF-8. Это еще одно различие между BSD и GNU. В зависимости от типа файлов, с которыми вы имеете дело, он может вам не понадобиться.

По какой-то непонятной для меня причине версия grep нашла больше вхождений, чем версия find, поэтому я рекомендую использовать grep.

person mokagio    schedule 22.10.2020

Попробуйте эту команду:

/home/user/ directory - find ./ -type f \
-exec sed -i -e 's/a.example.com/b.example.com/g' {} \;
person Navneet    schedule 14.03.2016
comment
Привет. Пожалуйста, не просто сбрасывайте код в качестве ответа. Объясните, что вы делаете, чтобы пользователи могли понять, как решить проблему. Ваше здоровье. - person Cthulhu; 14.03.2016

Мы можем попробовать использовать более мощный ripgrep как

rg "BYE_BYE_TEXT" ./ --files-with-matches | xargs sed -i "s/BYE_BYE_TEXT/WELCOME_TEXT/g"

Потому что ripgrep хорошо находит, а sed отлично заменяет.

person Rounak Datta    schedule 05.04.2021
comment
Есть ли возможность заставить эту работу работать и под Windows (git bash, если вам интересно)? rg продолжает использовать обратную косую черту, sed это не нравится (в основном их пропускает, поскольку в моем случае ни один из них не соответствует escape-коду, я думаю) и не работает с Нет такого файла или директории. Я мог sed заменить обратную реакцию на косую черту, но rg параметры в целом упростили бы задачу. На данный момент я использую rg "inpattern" ./ --files-with-matches | sed "s|\\\|/|g" | xargs sed -i -b "s/inpattern/outpattern/g" - (-b / двоичный режим, чтобы ваши окончания строк оставались такими, какие они есть). - person mbx; 18.06.2021

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

find /path/to/searchdir/ -name "serachpatter" -type f | xargs sed -i 's/stringone/StrIngTwo/g'

Также, если вы хотите ограничить глубину рекурсии, вы также можете установить ограничения:

find /path/to/searchdir/ -name "serachpatter" -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's/stringone/StrIngTwo/g'

person akjprajapati    schedule 24.07.2017
comment
найти / путь / к / searchdir / -name serachpatter -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's / stringone / StrIngTwo / g' - person akjprajapati; 24.07.2017
comment
В будущем, пожалуйста, отредактируйте свой ответ, а не размещайте под ним комментарий. Кроме того, отформатируйте код соответствующим образом; не используйте для кода текст, отформатированный полужирным шрифтом. - person pat-s; 24.07.2017

это намного проще.

for i in `find *` ; do sed -i -- 's/search string/target string/g' $i; done

find i => будет перебирать все файлы в папке и во вложенных папках.

sed -i => заменит в файлах соответствующую строку, если она существует.

person gilad 1    schedule 18.03.2018