Найти и заменить внутри текстового файла командой Bash

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

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

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

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

Ответ 1

Самый простой способ - использовать 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,}

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

Ответ 2

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

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

Флаг -i говорит ему сделать замену на месте.

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

Ответ 3

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

Существует команда "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 для получения дополнительной информации об этом...

Ответ 4

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

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

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

Ответ 5

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 ':'.

Ответ 6

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

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

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

2), когда мое ограничение заключается в том, чтобы использовать его оболочкой script, тогда вместо 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 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 virtual domain to Apache 2 domain directory
......

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

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

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

<VirtualHost ServerName www.example.com

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

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

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

Ответ 7

Вы можете использовать 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' {} \;

Ответ 8

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

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

Подробнее о bash -hackers site

Ответ 10

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

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

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

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

Ответ 11

Чтобы редактировать текст в файле неинтерактивно, вам нужен текстовый редактор на месте, например, vim.

Вот простой пример, как использовать его из командной строки:

vim -esnc '%s/foo/bar/g|:wq' file.txt

Это эквивалентно ответу @slim бывшего редактора, что в основном то же самое.

Вот несколько ex практических примеров.

Замена текста foo на bar в файле:

ex -s +%s/foo/bar/ge -cwq file.txt

Удаление конечных пробелов для нескольких файлов:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

Устранение неисправностей (когда терминал застрял):

  • Добавьте параметр -V1 для отображения подробных сообщений.
  • Принудительное -cwq!: -cwq! ,

Смотрите также:

Ответ 12

Попробуйте следующую команду оболочки:

find ./  -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'

Ответ 13

Вы также можете использовать python в bash script. Я не имел большого успеха с некоторыми из лучших ответов здесь, и нашел, что это работает без необходимости в циклах:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()

Ответ 14

Вы можете использовать команду rpl. Например, вы хотите изменить доменное имя во всем проекте php.

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

Это не ясно bash причины, но это очень быстро и полезно.:)