Как сделать рекурсивный поиск / замену строки с помощью awk или sed?

Как мне найти и заменить каждое вхождение:

subdomainA.example.com

с

subdomainB.example.com

в каждом текстовом файле под /home/www/ дерево каталогов рекурсивно?

37 ответов

Примечание. Не запускайте эту команду в папке, включающей git-репо - изменения в.git могут повредить ваш индекс git.

find /home/www -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

От man find:

-print0 (только для GNU-поиска) указывает find использовать нулевой символ (\0) вместо пробела в качестве выходного разделителя между найденными путями. Это более безопасный вариант, если ваши файлы могут содержать пробелы или другие специальные символы. Рекомендуется использовать аргумент -print0, чтобы узнать, используете ли вы команду -exec или xargs (аргумент -0 необходим в xargs.).

Самый простой способ для меня это

grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'

Примечание. Не запускайте эту команду в папке, включающей git-репо - изменения в.git могут повредить ваш индекс git.

find /home/www/ -type f -exec \
    sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

По сравнению с другими ответами здесь, это проще, чем большинство и использует sed вместо perl, что и было задано в оригинальном вопросе.

Все трюки почти одинаковы, но мне нравится этот:

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>Посмотрите в каталоге.

  • -type f:

    Тип файла: обычный файл

  • -exec command {} +:

    Этот вариант действия -exec запускает указанную команду для выбранных файлов, но командная строка создается путем добавления каждого выбранного имени файла в конце; общее количество вызовов команды будет намного меньше, чем количество совпавших файлов. Командная строка создается так же, как xargs создает свои командные строки. В команде допускается только один экземпляр `{}'. Команда выполняется в начальном каталоге.

Для меня самое простое решение запомнить это /questions/5680828/kak-ya-mogu-najti-i-zamenit-rekursivno-v-kataloge-v-vim/5680839#5680839, то есть:

sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f)

ПРИМЕЧАНИЕ: -i '' решает проблему OSX sed: 1: "...": invalid command code .

ПРИМЕЧАНИЕ. Если файлов слишком много для обработки, вы получите Argument list too long, Обходной путь - использование find -exec или же xargs Решение описано выше.

cd /home/www && find . -type f -print0 |
  xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

Для тех, кто использует серебряный искатель (ag)

ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'

Так как ag по умолчанию игнорирует файл / папки git/hg/svn, запуск в хранилище безопасен.

Это совместимо с git-репозиториями и немного проще:

Linux:

git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'

Mac:

git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g'

(Спасибо http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/)

Прямой метод, если вам нужно исключить каталоги (--exclude-dir=.svn) и также могут иметь имена файлов с пробелами (используя 0Byte с grep -Z а также xargs -0

grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'

Вырубать файлы рекурсивно sed через, вы могли бы grep для вашего экземпляра строки:

grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

Если вы бежите man grep вы заметите, что вы также можете определить --exlude-dir="*.git" флаг, если вы хотите пропустить поиск по каталогам.git, избегая проблем с индексами git, как вежливо указали другие.

Ведущий к вам:

grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

Один хороший приятель в качестве дополнительного. Используя git grep.

git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"

Самый простой способ заменить (все файлы, каталог, рекурсивно)

find . -type f -not -path '*/\.*' -exec sed -i 's/foo/bar/g' {} +

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

Если вы хотите включить использование скрытых файлов,

find . -type f  -exec sed -i 's/foo/bar/g' {} +

В обоих случаях строка foo будет заменен новой строкой bar

find /home/www/ -type f -exec perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

find /home/www/ -type f перечислит все файлы в /home/www/ (и его подкаталогах). Флаг "-exec" указывает find выполнять следующую команду для каждого найденного файла.

perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

команда запускается для файлов (много за раз). {} заменяется именами файлов. + в конце команды говорит find построить одну команду для многих имен файлов.

В соответствии с find Страница man: "Командная строка создается так же, как xargs создает свои командные строки".

Таким образом, можно достичь своей цели (и обрабатывать имена файлов, содержащие пробелы) без использования xargs -0, или же -print0,

Или используйте невероятно быстрый GNU Parallel:

grep -rl oldtext . | parallel sed -i 's/oldtext/newtext/g' {}

Я просто нуждался в этом и не был доволен скоростью доступных примеров. Итак, я придумал свое:

cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

Ack-grep очень эффективен при поиске соответствующих файлов. Эта команда заменила ~145 000 файлов на ветер, в то время как другие заняли так много времени, что я не мог дождаться их окончания.

grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done

Я предполагаю, что большинство людей не знают, что они могут что-то перенаправить в "время чтения файла", и это избегает этих неприятных аргументов -print0, сохраняя пробелы в именах файлов.

Дальнейшее добавление echo прежде чем Sed позволяет вам увидеть, какие файлы будут изменены, прежде чем делать это.

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

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`

Согласно этому сообщению в блоге:

find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'
#!/usr/local/bin/bash -x

find * /home/www -type f | while read files
do

sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p')

    if [ "${sedtest}" ]
    then
    sed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmp
    mv "${files}".tmp "${files}"
    fi

done

Вы можете использовать awk, чтобы решить эту проблему, как показано ниже,

for file in `find /home/www -type f`
do
   awk '{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}' $file > ./tempFile && mv ./tempFile $file;
done

Надеюсь, что это поможет вам!!!

Для замены всех вхождений в репозитории git вы можете использовать:

git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

Смотрите список файлов в локальном git-репо? для других опций перечислить все файлы в хранилище. -z options указывает git разделять имена файлов нулевым байтом, что гарантирует xargs (с возможностью -0) может разделять имена файлов, даже если они содержат пробелы или еще много чего.

Если вы не против использования vim вместе с grep или же find инструменты, вы можете следить за ответом, данным пользователем Gert в этой ссылке -> Как сделать замену текста в большой иерархии папок?,

Вот сделка:

  • рекурсивно grep для строки, которую вы хотите заменить в определенном пути, и взять только полный путь соответствующего файла. (это было бы $(grep 'string' 'pathname' -Rl),

  • (необязательно), если вы хотите сделать предварительное резервное копирование этих файлов в централизованном каталоге, возможно, вы также можете использовать это: cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'

  • после этого вы можете редактировать / заменять по желанию в vim по схеме, аналогичной той, которая указана в приведенной ссылке:

    • :bufdo %s#string#replacement#gc | update

Просто чтобы избежать также

  • NearlysubdomainA.example.com
  • subdomainA.example.comp.other

но до сих пор

  • subdomainA.example.com.IsIt.good

(возможно, не очень хорошо в идее корня домена)

find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;

Немного старой школы, но это работало на OS X.

Есть несколько хитростей:

• Будут редактировать только файлы с расширением .sls в текущем каталоге

. необходимо избежать, чтобы обеспечить sed не оценивает их как "любой персонаж"

, используется в качестве sed разделитель вместо обычного /

Также обратите внимание, что это редактировать шаблон Jinja для передачи variable на пути import (но это не по теме).

Во-первых, убедитесь, что ваша команда sed делает то, что вы хотите (это только напечатает изменения в stdout, но не изменит файлы):

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

При необходимости измените команду sed, как только вы будете готовы внести изменения:

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed -i '' 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

Обратите внимание -i '' в команде sed я не хотел создавать резервную копию исходных файлов (как объяснено в Редактировании на месте с помощью sed в OS X или в комментарии Роберта Лужо на этой странице).

С днем ​​рождения!

Если вы хотите использовать это без полного уничтожения вашего SVN-репозитория, вы можете указать 'find' игнорировать все скрытые файлы, выполнив:

find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'

Вот версия, которая должна быть более общей, чем большинство; это не требует find (с помощью du вместо), например. Это требует xargs, которые встречаются только в некоторых версиях Plan 9 (например, 9front).

 du -a | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

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

 du -a | grep "\.scala$" | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`

Я просто использую топы:

find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 |  xargs -0 tops -verbose  replace "verify_noerr(<b args>)" with "__Verify_noErr(<args>)" \
replace "check(<b args>)" with "__Check(<args>)" 

Используя комбинацию grep а также sed

for pp in $(grep -Rl looking_for_string)
do
    sed -i 's/looking_for_string/something_other/g' "${pp}"
done

Изменить несколько файлов (и сохранить резервную копию как *.bak):

perl -p -i -e "s/\|/x/g" *

возьмет все файлы в каталоге и заменит | с х называется "пирог Perl" (просто как пирог)

Другие вопросы по тегам