Как сделать рекурсивный поиск/замену строки с помощью 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' {} +
Ответ 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"
Ответ 10
Этот совместим с репозиториями 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/)
Ответ 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'