Как найти и заменить каждое вхождение:
subdomainA.example.com
с
subdomainB.example.com
в каждом текстовом файле в дереве каталогов /home/www/
рекурсивно?
Как найти и заменить каждое вхождение:
subdomainA.example.com
с
subdomainB.example.com
в каждом текстовом файле в дереве каталогов /home/www/
рекурсивно?
или используйте невероятно быстрый параллельный GNU:
grep -rl oldtext . | parallel sed -i 's/oldtext/newtext/g' {}
как установить GNU Parallel?
попробуйте найти параллельный пакет. арка: sudo pacman -S parallel
; ubuntu / debian: sudo apt-get install parallel
; шляпа-шляпа: dnf install parallel
; Я использую арку кстати
Самый простой способ заменить ( все файлы, каталог, рекурсивный )
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
Простой метод, если вам нужно исключить каталоги (--exclude-dir=..folder
), а также могут иметь имена файлов с пробелами (решено с использованием 0Byte для 41 <2800> и xargs -0
>)
grep -rlZ oldtext . --exclude-dir=.folder | xargs -0 sed -i 's/oldtext/newtext/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'
Я просто использую топы:
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>)"
плюс один для `'*. [c | cc | cp | cpp | m | mm | h]'`
Согласно этому сообщению в блоге:
find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'
Как избежать косой черты /
?. Например, я хочу заменить IP-адреса: xxx.xxx.xxx.xxx
на xxx.xxx.xxx.xxx/folder
Вы можете избежать /
с помощью \. Например: find . -type f | xargs perl -pi -e 's/xxx.xxx.xxx.xxx\/folder/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
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
Не используется awk
/ sed
, но часто используется perl (кроме встроенных / только систем с busybox).
просто чтобы не менять также
, но все же
(возможно, не очень подходит идея корневого домена)
find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;
Для замены всех вхождений в репозитории git вы можете использовать:
git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
См. Список файлов в локальном репозитории git?, чтобы узнать о других параметрах для вывода списка всех файлов в репозитории. Параметры -z
указывают git разделять имена файлов нулевым байтом, что гарантирует, что xargs
(с параметром -0
) может разделять имена файлов, даже если они содержат пробелы или еще много чего.
Если у вас есть доступ к узлу, вы можете сделать npm install -g rexreplace
, а затем
rexreplace 'subdomainA.example.com' 'subdomainB.example.com' /home/www/**/*.*
Чтобы заменить все содержимое, соответствующее string_1 на string_2 всех .c и и <6806149842280> в текущем каталоге и <6806149842280> вложенных файлах. > (кроме .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' {} +
Немного старой школы, но это работало на 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 или в комментарии Роберта Луджо на этой странице).
С праздником, народ!
Для всех, кто использует поисковик серебра (ag
)
ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'
Поскольку по умолчанию ag игнорирует файлы / папки git / hg / svn, его можно безопасно запускать внутри репозитория.
Спасибо за рабочее решение! Мне нужно будет найти эквивалент с помощью ripgrep.
@reductivity Проверьте github.com/chmln/sd :) Я счастливый пользователь
Замена ag на rg для ripgrep тоже отлично работает.
Вы можете использовать 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
надеюсь, это поможет вам !!!
Работает на MacO без проблем! Все команды на основе sed
завершились ошибкой, когда двоичные файлы были включены, даже с конкретными настройками osx.
Осторожно ... это взорвется, если какой-либо из возвращаемых файлов find
будет содержать пробел в своем имени! Намного безопаснее использовать while read
: coderhelper.com/a/9612560/1938956
Этот совместим с репозиториями 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/)
Разумнее использовать параметр git-grep
-z
вместе с xargs -0
.
git grep
очевидно имеет смысл только в репо git
. Общая замена - grep -r
.
@gniourf_gniourf Вы можете объяснить?
@PetrPeller: с -z
, git-grep
будет разделять поля вывода нулевыми байтами вместо символов новой строки; а с -0
, xargs
будет читать ввод, разделенный нулевыми байтами, вместо пробелов (и не делать странных вещей с кавычками). Поэтому, если вы не хотите, чтобы команда прерывалась, если имена файлов содержат пробелы, кавычки или другие забавные символы, используйте следующую команду: git grep -z -l 'original_text' | xargs -0 sed ...
.
Это лучшее универсальное решение, которое я нашел для OSX и Windows (msys2). Должен работать со всем, что может получить версию sed для gnu. Пропускает каталоги .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>
grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done
Думаю, большинство людей не знают, что они могут передать что-то в "файл во время чтения", и это позволяет избежать этих неприятных аргументов -print0, сохраняя при этом пробелы в именах файлов.
Дальнейшее добавление echo
перед sed позволяет вам увидеть, какие файлы будут изменены, прежде чем это делать.
Причина, по которой -print0
полезен, заключается в том, что он обрабатывает случаи, которые while read
просто не может обработать - новая строка является допустимым символом в имени файла Unix, поэтому для того, чтобы ваш код был полностью надежным, он должен справляться с такими именами файлов , слишком. (Кроме того, вы хотите read -r
, чтобы избежать неприятного поведения устаревшего POSIX в read
.)
Кроме того, sed
не работает, если нет совпадений, поэтому grep
на самом деле не требуется; хотя это полезная оптимизация, позволяющая избежать перезаписи файлов, которые не содержат совпадений, если у вас их много, или если вы хотите избежать ненужного обновления меток даты в файлах.
Если вы не против использования vim
вместе с инструментами grep
или find
, вы можете проследить ответ пользователя Герта по этой ссылке -> Как выполнить замену текста в большая иерархия папок?.
Вот сделка:
рекурсивно grep для строки, которую вы хотите заменить в определенном пути, и взять только полный путь соответствующего файла. (это будет $(grep 'string' 'pathname' -Rl)
.
(необязательно), если вы хотите сделать предварительную резервную копию этих файлов в централизованном каталоге, возможно, вы также можете использовать это: cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'
после этого вы можете редактировать / заменять по желанию в vim
, следуя схеме, аналогичной той, которая представлена по указанной ссылке:
:bufdo %s#string#replacement#gc | update
Использование комбинации grep
и sed
for pp in $(grep -Rl looking_for_string)
do
sed -i 's/looking_for_string/something_other/g' "${pp}"
done
@tripleee Я немного изменил это. В этом случае вывод для команды grep -Rl pattern
сгенерировал список файлов, в которых находится шаблон. Файлы не читаются в цикле for
.
Хм? У вас все еще есть цикл for
; если какое-либо возвращенное имя файла содержит пробелы, оно не будет работать правильно, поскольку оболочка токенизирует список аргументов for
. Но тогда вы используете переменную имени файла без кавычек внутри цикла, поэтому, если вы исправите это, она сломается там. Исправление этих оставшихся ошибок сделало бы ваш ответ идентичным ответу @ MadMan2064.
@tripleee да, это правда, я это пропустил.
Если вы хотите использовать это без полного разрушения репозитория SVN, вы можете указать find игнорировать все скрытые файлы, выполнив:
find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'
Скобки кажутся излишними. Ранее у него была ошибка форматирования, которая делала его непригодным для использования (рендеринг Markdown съедал некоторые символы из регулярного выражения).
Для 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
.
Самый простой способ для меня -
grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
Это особенно хорошо работает, когда вам нужно исключить каталоги, например, с .svn
. Например: grep -rl oldtext . --exclude-dir=.svn | xargs sed -i 's/oldtext/newtext/g'
В macOS sed -i
вызывает sed: 1: "file_path": invalid command code .
. Это потому, что -i - это другой флаг в macOS. Нашел grep -rl old . | xargs sed -i "" -e 's/old/new/g'
работает. Я нашел этот полезным
Если вы используете скомпилированный язык и не хотите проверять двоичные файлы, вы можете передать флаг I, например grep -Irl oldtext . | xargs sed -i 's/oldtext/newtext/g'
В проекте git обязательно используйте git grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
, чтобы избежать поиска зависимостей (которые, вероятно, игнорируются через .gitignore) :) Отличное решение! @phyatt, это лучший способ сделать это.
используя MACOS и разочарованный, почему он не работает -> попробуйте -> grep -rl 'SEARCHSTRING' ./ | LC_ALL=C xargs sed -i '' 's/SEARCHSTRING/REPLACESTRING/g'
И еще один приятный вкладыш в качестве дополнения. Используя git grep.
git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
Хорошая идея, если вы работаете внутри репозитория git, поскольку вы не рискуете перезаписать .git / contents (как указано в комментариях к другому ответу).
Спасибо, я использую его как функцию bash. refactor() { echo "Replacing $1 by $2 in all files in this git repository." git grep -lz $1| xargs -0 perl -i'' -pE "s/$1/$2/g" }
Использование, например, чтобы заменить слово "меч": refactor word sword
, а затем проверить, что он сделал с помощью git diff
.
Все приемы почти одинаковы, но мне нравится вот этот:
find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
find <mydir>
: поиск в каталоге.
-type f
:
Тип файла: обычный файл
-exec command {} +
:
Этот вариант действия -exec запускает указанную команду для выбранных файлов, но командная строка создается путем добавления имя каждого выбранного файла в конце; общее количество вызовов команды будет намного меньше, чем количество совпадающие файлы. Командная строка строится почти так же, как xargs строит свои командные строки. Только один экземпляр В команде допускается использование символа {}. Команда выполняется в начальном каталоге.
#!/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
Для меня проще всего запомнить https://coderhelper.com/a/2113224/565525, то есть:
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
решение, описанное выше.
для изменения нескольких файлов (и сохранения резервной копии как *.bak
):
perl -p -i -e "s/\|/x/g" *
возьмет все файлы в каталоге и заменит |
на x
называется «Perl pie» (простой как пирог)
Однако не рекурсивно через каталоги.
к нему можно подключиться по конвейеру, что делает его очень настраиваемым, в том числе через каталоги. josephscott.org/archives/2005/08/… и unix.stackexchange.com/questions/101415/…
Мне просто это было нужно, и я был недоволен скоростью доступных примеров. Так что я придумал свой:
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 -Hr 'subdomainA' * | cut -d: -f1
.
@Henno: только один вопрос: как я могу исключить двоичные файлы (исполняемые файлы) ?
ack-grep сделает это автоматически.
@Henno: Включает ли он сценарии оболочки?
да. Вот полный список поддерживаемых типов файлов: yondgrep.com/documentation
Попробуйте это:
sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
Привет @RikHic, хороший совет - думал о чем-то вроде этого; к сожалению, это форматирование, приведенное выше, не совсем удачно :) Так что я попробую с предварительным тегом (не работает) - так что с экранированием обратных кавычек: sed -i 's/subdomainA/subdomainB/g'
`grep -ril 'subdomainA' /home/www/*
` - это все равно не выглядит все тоже хорошо, но должен пережить копипасту :) Ура!
Более простой способ - использовать следующую команду в командной строке
find /home/www/ -type f|xargs perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
Примечание : не запускайте эту команду в папке, содержащей репозиторий git - изменения в .git могут повредить ваш индекс git.
find /home/www/ -type f -exec \
sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
По сравнению с другими ответами здесь, это проще, чем большинство, и использует sed вместо perl, что и было задано в исходном вопросе.
Обратите внимание, что если вы используете BSD sed (в том числе в Mac OS X), вам необходимо указать явный аргумент пустой строки для параметра sed -i
. например: sed -i '' 's/original/replacement/g'
Как я могу изменить его, чтобы исключить подпапку .git?
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
:
"Командная строка построена примерно так же, как
xargs создает свои командные строки. "
Таким образом, можно достичь своей цели (и обрабатывать имена файлов, содержащие пробелы) без использования xargs -0
или -print0
.
find /home/www \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
-print0
указывает find
печатать каждый результат, разделенный нулевым символом, а не новой строкой. В том маловероятном случае, если в вашем каталоге есть файлы с символами новой строки в именах, это все равно позволяет xargs
работать с правильными именами файлов.
\( -type d -name .git -prune \)
- это выражение, которое полностью пропускает все каталоги с именем .git
. Вы можете легко расширить его, если используете SVN или имеете другие папки, которые хотите сохранить - просто сравните с другими именами. Это примерно эквивалентно -not -path .git
, но более эффективно, потому что вместо проверки каждого файла в каталоге он полностью его пропускает. -o
после него требуется из-за того, как -prune
на самом деле работает.
Для получения дополнительной информации см. man find
.
Это сработало для меня, и мой случай заключался в поиске / замене значений IP-адреса. Однако вопрос к галерее: почему точки экранируются для первого значения subdomainA\.example\.com
, но не для второго значения sudomainB.example.com
? Я выполнил его в предложенном формате, и, похоже, он отлично справился со своей задачей, но мне любопытно, почему экранирование представлено только для первого строкового шаблона.
Этот сценарий остановится, не дойдя до конца, с ошибкой Permission denied
, если один из файлов имеет неизменяемый флаг. Лучше использовать -exec sed -i ... {} \;
вместо трубы.
Я часто использую find . -type f -print0 | xargs -0 sed -i -e 's/\r$//'
для рекурсивной замены всех CRLF на LF в файлах в определенном каталоге.
Это работает для Ubuntu с мая 2021 года.
используя MACOS и разочарованный, почему он не работает -> попробуйте -> find . \( ! -regex '.*/\..*' \) -type f | LC_ALL=C xargs sed -i '' 's/foo/bar/g'
Совет: не делайте нижеприведенное в дереве проверки svn ... это перезапишет файлы волшебной папки .svn.
Боже мой, это именно то, что я только что сделал. Но это сработало и, похоже, не причинило никакого вреда. Что худшего могло случиться?
@ J.Katzwinkel: по крайней мере, это может повредить контрольные суммы, что может повредить ваш репозиторий.
Совет для всех, кто использует sed: он добавит завершающие символы новой строки в ваши файлы. Если они вам не нужны, сначала выполните поиск-замену, которая ничего не найдет, и зафиксируйте это в git. Тогда сделай настоящий. Затем интерактивно переустановите и удалите первый.
@funroll Или используйте инструмент, который не требует принудительного добавления новой строки. например Perl; или принять тот факт, что POSIX строго требует, чтобы текстовые файлы имели окончания строк.
Вы можете исключить каталог, например git, из результатов, используя
-path ./.git -prune -o
вfind . -path ./.git -prune -o -type f -name '*matchThisText*' -print0
перед подключением к xargsЭтот ответ на веб-сайте Unix StackExchange тоже довольно хорош: unix.stackexchange.com/a/112024/354626
Альтернативное решение от SuperUser Как я могу выполнить рекурсивный поиск и замену из командной строки?:
find . -type f \( -iname \*.ht* -o -iname \*.php \) -exec sed -i'' -e 's/findString/replString/g' {} +
(строки должны быть экранированы, например, точки типа\.
) Могут игнорироваться имена файлов с пробелами (?)