Найти и заменить внутри текстового файла с помощью команды Bash - PullRequest
481 голосов
/ 08 февраля 2009

Какой самый простой способ найти и заменить данную входную строку, скажем, abc, и заменить другой строкой, скажем, XYZ в файле /tmp/file.txt?

Я пишу приложение и использую IronPython для выполнения команд через SSH & mdash; но я не очень хорошо знаю Unix и не знаю, что искать.

Я слышал, что Bash, помимо интерфейса командной строки, может быть очень мощным языком сценариев. Итак, если это правда, я предполагаю, что вы можете выполнять такие действия.

Могу ли я сделать это с помощью bash, и какой самый простой (однострочный) скрипт для достижения моей цели?

Ответы [ 14 ]

811 голосов
/ 08 февраля 2009

Самый простой способ - использовать sed (или perl):

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

Который будет вызывать sed для редактирования на месте из-за опции -i. Это можно вызвать из bash.

Если вы действительно хотите использовать только bash, то может сработать следующее:

while read a ; do echo ${a//abc/XYZ} ; done < /tmp/file.txt > /tmp/file.txt.t ; mv /tmp/file.txt{.t,}

Это зацикливает каждую строку, делая подстановку и записывая во временный файл (не желая засорять ввод) Движение в конце просто временно перемещается к исходному имени.

156 голосов
/ 08 февраля 2009

Работа с файлами обычно выполняется не Bash, а программами, вызываемыми Bash, например ::100100

> perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

Флаг -i указывает ему выполнить замену на месте.

См. man perlrun для получения дополнительной информации, в том числе о том, как сделать резервную копию исходного файла.

65 голосов
/ 09 апреля 2013

Я был удивлен, потому что я наткнулся на это ...

Существует команда "replace" , которая поставляется с пакетом "mysql-server" , поэтому, если вы установили ее, попробуйте:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

См. man replace , чтобы узнать больше об этом ...

40 голосов
/ 07 декабря 2015

Это старый пост, но для тех, кто хочет использовать переменные, как @centurian сказал, что одинарные кавычки означают, что ничего не будет расширено.

Простой способ получить переменные - это конкатенация строк, так как это делается путем сопоставления в bash, должно работать следующее:

sed -i -e 's/'"$var1"'/'"$var2"'/g' /tmp/file.txt

38 голосов
/ 08 февраля 2009

Bash, как и другие оболочки, является просто инструментом для координации других команд. Обычно вы пытаетесь использовать стандартные команды UNIX, но вы, конечно, можете использовать Bash для вызова чего угодно, включая ваши собственные скомпилированные программы, другие сценарии оболочки, сценарии Python и Perl и т. Д.

В этом случае есть несколько способов сделать это.

Если вы хотите прочитать файл и записать его в другой файл, выполняя поиск / замену на ходу, используйте sed:

sed 's/abc/XYZ/g' <infile >outfile

Если вы хотите отредактировать файл на месте (как будто открываете файл в редакторе, редактируете его, затем сохраняете), предоставьте инструкции для редактора строк 'ex'

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex похож на vi без полноэкранного режима. Вы можете дать ему те же команды, что и в приглашении vi ':'.

32 голосов
/ 07 февраля 2013

Нашел эту ветку среди других, и я согласен, что она содержит наиболее полные ответы, поэтому я также добавляю свои:

1) sed и ed так полезны ... вручную !!! Посмотрите на этот код от @Johnny:

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

2) когда мое ограничение заключается в использовании его сценарием оболочки, никакая переменная не может использоваться внутри вместо abc или XYZ! Это , кажется, согласуется с тем, что я понимаю, по крайней мере. Итак, я не могу использовать:

x='abc'
y='XYZ'
sed -i -e 's/$x/$y/g' /tmp/file.txt
#or,
sed -i -e "s/$x/$y/g" /tmp/file.txt

но что мы можем сделать? Как сказал @Johnny, «пока читаешь ...», но, к сожалению, это еще не конец истории. Следующие хорошо работали со мной:

#edit user's virtual domain
result=
#if nullglob is set then, unset it temporarily
is_nullglob=$( shopt -s | egrep -i '*nullglob' )
if [[ is_nullglob ]]; then
   shopt -u nullglob
fi
while IFS= read -r line; do
   line="${line//'<servername>'/$server}"
   line="${line//'<serveralias>'/$alias}"
   line="${line//'<user>'/$user}"
   line="${line//'<group>'/$group}"
   result="$result""$line"'\n'
done < $tmp
echo -e $result > $tmp
#if nullglob was set then, re-enable it
if [[ is_nullglob ]]; then
   shopt -s nullglob
fi
#move user's virtual domain to Apache 2 domain directory
......

3) Как видно, установлен ли тогда nullglob, он ведет себя странно, когда есть строка, содержащая *, как в

<VirtualHost *:80>
 ServerName www.example.com

, который становится

<VirtualHost ServerName www.example.com

нет конечной угловой скобки, и Apache2 даже не может загрузить!

4) Этот вид синтаксического анализа должен выполняться медленнее, чем поиск и замена одним нажатием, но, как вы уже видели, есть 4 переменные для 4 различных шаблонов поиска, работающих на один цикл анализа!

Наиболее подходящее решение, которое я могу придумать с учетом предположений о проблеме.

19 голосов
/ 14 января 2017

Вы можете использовать sed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

Используйте -i для игнорировать регистр , если вы не уверены, что текст для поиска - abc или ABC или AbC и т. Д.

Вы можете использовать find и sed, если у вас нет имени файла:

 find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

Найти и заменить во всех файлах Python:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;
11 голосов
/ 14 июня 2009

Вы также можете использовать команду ed для поиска в файле и замены:

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

Подробнее на сайте bash-hackers

8 голосов
/ 15 апреля 2017

Если файл, с которым вы работаете, не такой большой, и временное сохранение его в переменной не представляет проблемы, тогда вы можете использовать подстановку строк Bash для всего файла сразу - нет необходимости перебирать его построчно :

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

Все содержимое файла будет обрабатываться как одна длинная строка, включая разрывы строк.

XYZ может быть переменной, например, $replacement, и одним из преимуществ не использования sed здесь является то, что вам не нужно беспокоиться о том, что строка поиска или замены может содержать символ разделителя шаблона sed (обычно, но не обязательно, /) , Недостатком является невозможность использования регулярных выражений или каких-либо более сложных операций sed.

8 голосов
/ 27 июля 2015

Будьте осторожны, если вы заменяете URL символом "/".

Пример того, как это сделать:

sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"

Извлечено из: http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

...