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

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

subdomainA.example.com

с

subdomainB.example.com

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

Ответ 1

find /home/www \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

От man find:

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>: найдите в каталоге.

  • -type f:

    Файл имеет тип: обычный файл

  • -exec command {} +:

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

Ответ 5

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

Ответ 6

Для меня самое легкое решение для запоминания - fooobar.com/questions/2977/..., то есть:

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 решение, описанное выше.

Ответ 7

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

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

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

Ответ 8

Чтобы сократить рекурсивные файлы 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

Ответ 9

Один приятный oneliner как дополнительный. Использование git grep.

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

Ответ 11

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.

Ответ 12

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

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 файлов ветром, в то время как другие заняли так много времени, что я не мог дождаться их завершения.

Ответ 13

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

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

Ответ 14

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

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

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

Ответ 15

Вы можете использовать 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

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

Ответ 16

Попробуйте следующее:

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

Ответ 17

#!/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

Ответ 18

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

Здесь сделка:

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

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

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

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

Ответ 19

Немного старой школы, но это работало на 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 или в комментарии Роберта Луджо на этой странице).

Счастливые люди седанов!

Ответ 20

чтобы избежать изменения.

  • 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' {} \;

Ответ 21

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

find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'

Ответ 22

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

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>)" 

Ответ 23

Здесь версия, которая должна быть более общей, чем большинство; например, он не требует 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'

Ответ 24

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

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

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

Ответ 25

Для Qshell (qsh) для IBMi, а не bash как помечено OP.

Ограничения команд qsh:

  • find не имеет опции -print0
  • xargs не имеет опции -0
  • sed не имеет опции -i

Таким образом, решение в qsh:

    PATH='your/path/here'
    SEARCH=\'subdomainA.example.com\'
    REPLACE=\'subdomainB.example.com\'

    for file in $( find ${PATH} -P -type f ); do

            TEMP_FILE=${file}.${RANDOM}.temp_file

            if [ ! -e ${TEMP_FILE} ]; then
                    touch -C 819 ${TEMP_FILE}

                    sed -e 's/'$SEARCH'/'$REPLACE'/g' \
                    < ${file} > ${TEMP_FILE}

                    mv ${TEMP_FILE} ${file}
            fi
    done

Предостережения:

  • Решение исключает обработку ошибок
  • Не bash помечается OP

Ответ 26

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

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

Ответ 27

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

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

Ответ 28

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

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

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

Ответ 29

perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`

Ответ 30

Проще всего использовать нижеследующее в командной строке

find /home/www/ -type f|xargs perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g'