Как найти и заменить каждое вхождение:
subdomainA.example.com
с
subdomainB.example.com
в каждом текстовом файле в дереве каталогов /home/www/
(рекурсивный поиск/замена).
Примечание. Не запускайте эту команду в папке, содержащей git repo - изменения в .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.).
sed: 1: "...": invalid command code .
проблема. Кажется, опция -i ожидает расширения и разбирает команду 's/../...'
. Решение: передайте расширение '' в опцию -i, например, sed -i '' 's/...
svn st
показывает изменений, это потому, что вы также изменили файлы в каталогах .svn! Используйте find . -maxdepth 1 -type f -print0 | xargs -0 sed -i 's/toreplace/replaced/g'
.
Примечание. Не запускайте эту команду в папке, содержащей git repo - изменения в .git могут повредить ваш индекс git.
find /home/www/ -type f -exec \
sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
По сравнению с другими ответами здесь это проще, чем у большинства, и использует sed вместо perl, что и требовался исходный вопрос.
-i
. т.е.: sed -i '' 's/original/replacement/g'
Все трюки почти одинаковы, но мне нравится этот:
find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
find <mydir>
: найдите в каталоге.
-type f
:
Файл имеет тип: обычный файл
-exec command {} +
:
Этот вариант действия -exec выполняет указанную команду в выбранных файлах, но командная строка создается путем добавления каждое имя выбранного файла в конце; общее количество вызовов команды будет намного меньше, чем количество согласованные файлы. Командная строка построена почти так же, как xargs создает свои командные строки. Только один экземпляр `{} 'разрешено внутри команды. Команда запускается в стартовом каталоге.
cd /home/www && find . -type f -print0 |
xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
-print0
и xargs
вместо -exec
или -execdir
?
Самый простой способ для меня -
grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
-I
или --binary-file=without-match
флаги grep --binary-file=without-match
.
Для меня самое легкое решение для запоминания - https://stackoverflow.com/questions/2113111/how-can-i-search-and-replace-recursively-in-a-directory-in-vim, то есть:
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
решение, описанное выше.
workaround
должен быть предпочтительным синтаксисом во всех случаях.
$(find...)
заключается в том, что оболочка не может обработать имена файлов с пробелами или другими метасимволами оболочки. Если вы знаете, что это не проблема, такой подход хорош; но у нас слишком много вопросов, по которым люди не были предупреждены об этой проблеме или не поняли предупреждение.
Для тех, кто использует серебряный искатель (ag
)
ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'
Так как ag игнорирует файл git/hg/svn файл/папки по умолчанию, это безопасно запускать внутри репозитория.
Один приятный oneliner как дополнительный. Использование git grep.
git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
Этот совместим с репозиториями 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/)
xargs -0
опцию -z
для git-grep
вместе с xargs -0
.
git grep
очевидно, имеет смысл только в git
repo. Общая замена будет grep -r
.
Я просто нуждался в этом и не был доволен скоростью доступных примеров. Поэтому я придумал свой собственный:
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 -ril 'subdomainA' *
, но grep -ril 'subdomainA' *
далеко не так быстр, как grep -Hr 'subdomainA' * | cut -d: -f1
.
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
.
Чтобы сократить рекурсивные файлы 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
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 позволит вам увидеть, какие файлы будут изменяться, прежде чем делать это.
-print0
полезна в том, что она обрабатывает случаи, которые в while read
просто не может обработать - -print0
является допустимым символом в имени файла Unix, поэтому для того, чтобы ваш код был полностью устойчивым, он должен также справляться с такими именами файлов , (Кроме того, вы хотите, чтобы read -r
избегал какого-то досадного устаревшего поведения POSIX при read
.)
sed
не используется, если нет совпадений, поэтому grep
самом деле не нужен; хотя это полезная оптимизация, позволяющая избежать перезаписи файлов, которые не содержат совпадений, если у вас их много или вы хотите без необходимости обновлять отметки даты на файлах.
#!/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
Попробуйте следующее:
sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
sed -i 's/subdomainA/subdomainB/g'
` grep -ril 'subdomainA' /home/www/*
`- это все равно выглядит не слишком хорошо, но должно выдержать копирование пасты :) Ура!
Если вы не против использования 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
чтобы избежать изменения.
но все же
(может быть, не очень хорошо в идее, лежащей в основе корня домена)
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 или в комментарии Роберта Луджо на этой странице).
Счастливые люди седанов!
Вы можете использовать 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
надеюсь, что это поможет вам!
sed
не выполнялись, когда были включены двоичные файлы даже с особыми настройками osx.
find
возвращения имеют место в их именах! Гораздо безопаснее использовать while read
: stackoverflow.com/a/9612560/1938956
Используя комбинацию grep
и sed
for pp in $(grep -Rl looking_for_string)
do
sed -i 's/looking_for_string/something_other/g' "${pp}"
done
grep -Rl pattern
генерируется список файлов, в которых находится шаблон. Файлы не читаются в for
цикла.
Если вы хотите использовать это без полного уничтожения своего репозитория SVN, вы можете сказать "найти", чтобы игнорировать все скрытые файлы, выполнив:
find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'
Для Qshell (qsh) для IBMi, а не bash как помечено OP.
Ограничения команд qsh:
Таким образом, решение в 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
Предостережения:
for
.
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
awk
/ sed
, но распространен perl (за исключением встроенных систем / только с busybox).
Для замены всех вхождений в репозитории git вы можете использовать:
git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
Смотрите Список файлов в локальном git repo? для других опций, чтобы перечислить все файлы в репозитории. Опция -z
сообщает git отделить имена файлов с нулевым байтом, что гарантирует, что xargs
(с опцией -0
) может разделять имена файлов, даже если они содержат пробелы или что-то еще.
Если у вас есть доступ к node, вы можете сделать npm install -g rexreplace
, а затем
rexreplace 'subdomainA.example.com' 'subdomainB.example.com' /home/www/**/*.*
Чтобы заменить все соответствие содержания string_1 на string_2 всех файлов .c и .h в текущем каталог и подкаталоги (исключая .git/).
Это работает на Mac:
find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \
sed -i '' -e 's/'$1'/'$2'/g' {} +
Это должно работать на Linux (еще не протестировано):
find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \
sed -i 's/string_1/string_2/g' {} +
Это лучшее решение, которое я нашел для OSX и Windows (msys2). Должен работать со всем, что может получить версию gnu sed. Пропускает каталоги .git, поэтому он не повредит ваши контрольные суммы.
На mac, сначала установите coreutils и убедитесь, что gsed находится в пути -
brew install coreutils
Затем я использую эту функцию в моем zshrc/bashrc →
replace-recursive() {
hash gsed 2>/dev/null && local SED_CMD="gsed" || SED_CMD="sed"
find . -type f -name "*.*" -not -path "*/.git/*" -print0 | xargs -0 $SED_CMD -i "s/$1/$2/g"
}
usage: replace-recursive <find> <replace>
изменить несколько файлов (и сохранить резервную копию как *.bak):
perl -p -i -e "s/\|/x/g" *
возьмет все файлы в каталоге и заменит "|" с х называемый "Perl pie" (простой как пирог)
Проще всего использовать нижеследующее в командной строке
find /home/www/ -type f|xargs perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g'