Ответ 1
cd /path/to/your/folder
sed -i 's/foo/bar/g' *
Возникновение "foo" будет заменено на "bar".
Мне нужно заменить строку в большом количестве файлов в папке, только с ssh
доступом к серверу. Как я могу это сделать?
cd /path/to/your/folder
sed -i 's/foo/bar/g' *
Возникновение "foo" будет заменено на "bar".
Подобно Kaspar, но с флагом g, чтобы заменить все вхождения в строке.
find ./ -type f -exec sed -i 's/string1/string2/g' {} \;
Для глобального нечувствительного к регистру:
find ./ -type f -exec sed -i 's/string1/string2/gI' {} \;
@kev ответ хорош, но влияет только на файлы в непосредственной директории. В приведенном ниже примере используется grep для рекурсивного поиска файлов. Он работает для меня каждый раз.
grep -rli 'old-word' * | xargs [email protected] sed -i 's/old-word/new-word/g' @
Это сработало для меня:
find ./ -type f -exec sed -i 's/string1/string2/' {} \;
Howerver, этого не произошло: sed -i 's/string1/string2/g' *
. Возможно, "foo" не предназначался для string1 и "bar" not string2.
Есть несколько стандартных ответов на это уже перечисленные. Как правило, вы можете использовать find, чтобы рекурсивно перечислить файлы, а затем выполните операции с sed или perl.
Для наиболее быстрого использования вы можете найти команду rpl гораздо легче запомнить. Вот замена (foo → bar), рекурсивно на все файлы:
rpl -R foo bar .
Вам, вероятно, потребуется установить его (apt-get install rpl
или аналогичный).
Однако для более сложных заданий, которые включают регулярные выражения и обратную подстановку, или переименования файлов, а также поиск и замену, самым общим и мощным инструментом, о котором я знаю, является repren, небольшой Python script Я написал некоторое время назад для некоторых более сложных задач переименования и рефакторинга. Причины, по которым вы, возможно, предпочтете это:
Чтобы использовать его, pip install repren
. Проверьте пример README.
Чтобы заменить строку в нескольких файлах, вы можете использовать:
grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'
Например
grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
Чтобы заменить путь в файлах (избегая escape-символов), вы можете использовать следующую команду:
sed -i '[email protected][email protected][email protected]'
Знак @означает, что все специальные символы следует игнорировать в следующей строке.
Если в вашей строке есть косая черта (/), вы можете изменить разделитель на "+".
find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +
Эта команда будет выполняться рекурсивно в текущем каталоге.
Первая строка вхождения "foo" будет заменена на "bar". И вы можете использовать вторую строку для проверки.
grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c
Если у вас есть список файлов, которые вы можете использовать
replace "old_string" "new_string" -- file_name1 file_name2 file_name3
Если у вас есть все файлы, которые вы можете использовать
replace "old_string" "new_string" -- *
Если у вас есть список файлов с расширением, вы можете использовать
replace "old_string" "new_string" -- *.extension
"Вы также можете использовать find и sed, но я обнаружил, что эта маленькая строчка perl прекрасно работает.
perl -pi -w -e 's/search/replace/g;' *.php
"(Извлечено из http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php)
Мои лучшие результаты получены от использования perl и grep (чтобы убедиться, что файл имеет выражение поиска)
perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )
Я придумал свое решение, прежде чем нашел этот вопрос (и ответы). Я искал разные комбинации "replace" "несколько" и "xml", потому что это было мое приложение, но не нашло его.
Моя проблема: у меня были spring xml файлы с данными для тестовых примеров, содержащие сложные объекты. Рефакторинг в исходном коде java изменил много классов и не применился к файлам данных xml. Чтобы сохранить данные тестовых случаев, мне нужно было изменить все имена классов во всех xml файлах, распределенных по нескольким каталогам. Все при сохранении резервных копий исходных xml файлов (хотя это не было обязательным, так как контроль версий меня спасет здесь).
Я искал некоторую комбинацию find
+ sed
, потому что он работал у меня в других ситуациях, но не с несколькими заменами сразу.
Затем я нашел запрос ubuntu response, и это помогло мне создать мою командную строку:
find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
И это сработало отлично (ну, у моего дела было шесть разных замен). Обратите внимание, что он будет касаться всех *.xml файлов в текущем каталоге. Из-за этого, и если вы подотчетны системе управления версиями, вы можете сначала фильтровать и переходить только к sed
тем, у кого есть нужные строки; как:
find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
grep --include={*.php,*.html} -rnl './' -e "old" | xargs [email protected] sed -i 's/old/new/g' @
На MacBook Pro я использовал следующее (вдохновлено fooobar.com/questions/33995/...):
sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
-i ''
гарантирует, что вы не делаете никаких резервных копий.
-e
для современного регулярного выражения.
Редактор потока модифицирует несколько файлов "inplace" при вызове с помощью переключателя -i
, который берет файл резервной копии, заканчивающийся как аргумент. Так
sed -i.bak 's/foo/bar/g' *
заменяет foo
на bar
во всех файлах в этой папке, но не входит в подпапки. Однако это приведет к созданию нового файла .bak
для каждого файла в вашем каталоге.
Чтобы сделать это рекурсивно для всех файлов в этом каталоге и во всех его подкаталогах, вам понадобится помощник, например find
, для перемещения по дереву каталогов.
find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *
find
позволяет вам дополнительно ограничивать, какие файлы изменять, указав при необходимости дополнительные аргументы, например find ./ -name '*.php' -or -name '*.html' -print0
.
Примечание: GNU sed
не требует окончания файла, также будет работать sed -i 's/foo/bar/g' *
; FreeBSD sed
требует расширения, но позволяет пространство между ними, поэтому sed -i .bak s/foo/bar/g *
работает.
script для команды multiedit
multiedit [-n PATTERN] OLDSTRING NEWSTRING
Из ответа Kaspar я сделал bash script, чтобы принять аргументы командной строки и, возможно, ограничить имена файлов, соответствующие шаблону. Сохраните в своей $PATH и создайте исполняемый файл, а затем просто используйте команду выше.
Здесь script:
#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n
[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"
# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then # if -n option is given:
# replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
# replace OLDSTRING with NEWSTRING recursively in all files
find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi
Действительно хромой, но я не мог заставить любую команду sed работать прямо на OSX, поэтому вместо этого я сделал эту тупую вещь:
:%s/foo/bar/g
:wn
^ - скопируйте эти три строки в мой буфер обмена (да, включите окончание новой строки), затем:
vi *
и удерживайте команду-v, пока не сообщите, что файлов не осталось.
Тупой... Hacky... эффективный...
Если файл содержит обратные косые черты (обычно пути), вы можете попробовать что-то вроде этого:
sed -i -- 's,<path1>,<path2>,g' *
Пример:
sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)
Чтобы сохранить личный английский node, я написал утилиту script, которая поможет заменить несколько папок старой/новой строки, для всех файлов в каталоге рекурсивно.
Несколько паре старой/новой строки управляются в хэш-карте.
Диск может быть установлен с помощью командной строки или переменной окружения, карта жестко закодирована в script, но вы можете изменить код для загрузки из файла, если это необходимо.
Требуется bash 4.2, из-за некоторой новой функции.
en_standardize.sh:
#! /bin/bash
# (need bash 4.2+,)
#
# Standardize phonetic symbol of English.
#
# format:
# en_standardize.sh [<dir>]
#
# params:
# * dir
# target dir, optional,
# if not specified then use environment variable "$node_dir_en",
# if both not provided, then will not execute,
# *
#
paramCount=$#
# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
echo -e "dir specified (in command):\n\t$1\n"
targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
targetDir=$node_dir_en
else # environable not set,
echo "dir not specified, won't execute"
exit
fi
# check whether dir exists,
if [ -d $targetDir ]; then
cd $targetDir
else
echo -e "invalid dir location:\n\t$targetDir\n"
exit
fi
# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'
# do replace,
for key in "${!itemMap[@]}"; do
grep -rli "$key" * | xargs [email protected] sed -i "s/$key/${itemMap[$key]}/g" @
done
echo -e "\nDone."
exit
Использование команды ack будет намного быстрее:
ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'
Также, если у вас есть пробел в результатах поиска. Вы должны избежать этого.
Если вы хотите найти строку search
и заменить ее на replace
в нескольких файлах, это моя проверенная в бою формула в одну строку:
grep -RiIl 'search' | xargs sed -i 's/search/replace/g'
Быстрое объяснение grep:
-R
- рекурсивный поиск-i
- без учета регистра-I
- пропустить двоичные файлы (вам нужен текст, верно?)-l
- распечатывать простой список как вывод. Необходим для других командЗатем вывод grep передается в sed (через xargs), который фактически используется для замены текста. Флаг -i
изменит файл напрямую. Снимите его для своего рода "пробного запуска".
Команда ниже может быть использована для поиска файлов и замены файлов:
find . | xargs grep 'search string' | sed 's/search string/new string/g'
Например
find . | xargs grep abc | sed 's/abc/xyz/g'
Я приводил пример для исправления общей ошибки shebang в источниках python.
Вы можете попробовать подход grep/sed. Вот один из них, который работает с GNU sed и не разбивает репозиторий git:
$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}
Или вы можете использовать greptile:)
$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .
Я только что проверил первый script, а второй должен работать. Будьте осторожны с escape-символами, я думаю, что в большинстве случаев лучше использовать greptile. Конечно, вы можете делать много интересного с sed, и для этого может быть предпочтительнее использовать его с помощью xargs.
Я нашел это из другого сообщения (не могу вспомнить, какой), и хотя он не самый элегантный, он прост и как новичок, пользователь Linux не дал мне никаких проблем.
for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done
Если у вас есть пробелы или другие специальные символы, используйте \
for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
для строк в подкаталогах используйте **
for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
Существует более простой способ использования простого файла сценария:
# sudo chmod +x /bin/replace_string_files_present_dir
откройте файл в gedit или редакторе по вашему выбору, я использую gedit здесь.
# sudo gedit /bin/replace_string_files_present_dir
Затем в редакторе вставьте следующее в файл
#!/bin/bash
replace "oldstring" "newstring" -- *
replace "oldstring1" "newstring2" -- *
#add as many lines of replace as there are your strings to be replaced for
#example here i have two sets of strings to replace which are oldstring and
#oldstring1 so I use two replace lines.
Сохраните файл, закройте gedit, затем выйдите из терминала или просто закройте его, а затем запустите, чтобы иметь возможность загрузить новый скрипт, который вы добавили.
Перейдите в каталог, где у вас есть несколько файлов, которые вы хотите редактировать. Затем запустите:
#replace_string_files_present_dir
Нажмите клавишу ввода, и это автоматически заменит oldstring и oldstring1 во всех файлах, которые их содержат, на правильные newstring и newstring1 соответственно.
Он пропустит все каталоги и файлы, которые не содержат старых строк.
Это может помочь в устранении утомительной работы при наборе текста, если у вас есть несколько каталогов с файлами, которые требуют замены строки. Все, что вам нужно сделать, это перейти к каждому из этих каталогов, а затем выполнить:
#replace_string_files_present_dir
Все, что вам нужно сделать, это убедиться, что вы включили или добавили все строки замены, как я показал вам выше:
replace "oldstring" "newstring" -- *
в конце файла /bin/replace_string_files_present_dir.
Чтобы добавить новую строку замены, просто откройте созданный нами скрипт, набрав в терминале следующее:
sudo gedit /bin/replace_string_files_present_dir
Не беспокойтесь о количестве строк замены, которые вы добавляете, они не будут иметь эффекта, если oldstring не найдена.
$replace "From" "To" - `find/path/to/folder -type f`
(replace - утилита замены строк базы данных MySQL)