
Используя оболочку типа 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'
Вот как это работает:
find . -type f -name '*.txt'
находит в текущем каталоге (.
) и ниже все обычные файлы (-type f
), имена которых заканчиваются на.txt
|
передает вывод этой команды (список имен файлов) следующей командеxargs
собирает эти имена файлов и передает их по одномуsed
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
(например)