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

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

Используя оболочку типа bash или zshell, как мне сделать рекурсивный 'найти и заменить'? Другими словами, я хочу заменить каждое вхождение 'foo' на 'bar' во всех файлах в этом каталоге и его подкаталогах.

решение1

Эта команда сделает это (проверено на Mac OS X Lion и Kubuntu Linux).

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

Вот как это работает:

  1. find . -type f -name '*.txt'находит в текущем каталоге ( .) и ниже все обычные файлы ( -type f), имена которых заканчиваются на.txt
  2. |передает вывод этой команды (список имен файлов) следующей команде
  3. xargsсобирает эти имена файлов и передает их по одномуsed
  4. sed -i '' -e 's/foo/bar/g'означает «отредактировать файл на месте, без резервной копии, и выполнить следующую замену ( s/foo/bar) несколько раз в каждой строке ( /g)» (см man sed. )

Обратите внимание, что часть «без резервной копии» в строке 4 для меня приемлема, поскольку файлы, которые я изменяю, в любом случае находятся под контролем версий, поэтому я могу легко отменить изменения, если была допущена ошибка.

Чтобы не запоминать это, я использую интерактивный bash-скрипт, как показано ниже:

#!/bin/bash
# find_and_replace.sh

echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"

find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"

решение2

find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +

Это устраняет xargsзависимость.

решение3

Если вы используете Git, то вы можете сделать это:

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'

-lвыводит только имена файлов. -zПечатает нулевой байт после каждого результата.

В итоге я сделал это, потому что некоторые файлы в проекте не имели новой строки в конце файла, и sed добавлял новую строку, даже если не вносил никаких других изменений. (Никаких комментариев о том, должны ли файлы иметь новую строку в конце.

решение4

Вот моя функция zsh/perl, которую я для этого использую:

change () {
        from=$1 
        shift
        to=$1 
        shift
        for file in $*
        do
                perl -i.bak -p -e "s{$from}{$to}g;" $file
                echo "Changing $from to $to in $file"
        done
}

И я бы выполнил это с помощью

$ change foo bar **/*.java

(например)

Связанный контент