Как сделать рекурсивный поиск / замену строки с помощью 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" (просто как пирог)