9

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

7

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

subdomainA.example.com

на

subdomainB.example.com

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

5 ответ(ов)

10

Команда, которую вы представили, выглядит следующим образом:

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

Команда -print0 указывает find выводить каждый результат, разделяя их нулевым символом, а не символом новой строки. Это позволяет xargs корректно обрабатывать имена файлов даже в случае, если в них присутствуют символы новой строки.

Выражение \( -type d -name .git -prune \) полностью пропускает все директории с именем .git. Вы можете легко расширить это выражение, если используете SVN или у вас есть другие директории, которые вы хотите исключить — просто добавьте дополнительные имена для сопоставления. Это примерно эквивалентно -not -path .git, но более эффективно, так как вместо проверки каждого файла в директории, оно просто пропускает её целиком. Один из важнейших моментов — это то, что -o (или) после этого выражения требуется из-за особенностей работы -prune.

Для получения дополнительной информации вы можете обратиться к документации, выполнив man find.

5

Самый простой способ, по моему мнению, выглядит так:

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

Этот командный набор ищет рекурсивно все файлы в текущей директории (и подкаталогах), содержащие oldtext, и заменяет его на newtext во всех найденных файлах. Параметр -r указывает на рекурсивный поиск, -l выводит только имена файлов, а -Z обеспечивает корректную обработку файлов с пробелами в именах. Использование xargs -0 гарантирует, что имена файлов будут правильно переданы в sed.

3

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

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

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

1

Всё это похоже на другие приемы, но мне нравится этот:

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

Файл является обычным файлом.

  • -exec command {} +:

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

0

Самое простое решение, которое помогает мне его запомнить, это ссылка на ответ на StackOverflow: https://stackoverflow.com/a/2113224/565525, а именно:

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь