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

291

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

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

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

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

Теги:
scripting
ssh
replace
ironpython

14 ответов

510
Лучший ответ

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

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

  • 3
    За исключением того, что вызов mv в значительной степени такой же «non Bash», как и использование sed. Я почти сказал то же самое о эхо, но это встроенная оболочка.
  • 13
    Я знаю, что это немного позже, но для других Googlers: я не думаю, что вы хотите e там, если вы не хотите, чтобы ваши оригиналы с дополнительным e в конце. Вы можете пропустить это, и первый неопциональный аргумент будет интерпретирован как скрипт или do -i -e
Показать ещё 9 комментариев
136

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

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

Флаг -i указывает, что он выполняет замену на месте.

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

  • 33
    Пурист во мне говорит, что вы не можете быть уверены, что Perl будет доступен в системе. Но это очень редко в настоящее время. Возможно, я показываю свой возраст.
  • 2
    Можете ли вы показать более сложный пример. Что-то вроде замены "chdir / blah" на "chdir / blah2". Я пробовал perl -pi -e 's/chdir (?:\\/[\\w\\.\\-]+)+/chdir blah/g' text , но я получаю сообщение об ошибке из-за отсутствия пробела между шаблон и следующее слово не рекомендуется в строке -e 1. Не соответствует (в регулярном выражении; отмечен знаком <- HERE в m / (chdir) () (<- HERE?: \\ / at -e line 1.
Показать ещё 2 комментария
54

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

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

  • 1
    хорошее открытие, может пригодиться когда-нибудь
  • 1
    Это невероятно полезно. Я использую это прямо сейчас, чтобы создать мой скрипт установки L4 BASH.
Показать ещё 7 комментариев
33

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

  • 2
    Пример «ex» аккуратен, но не относится к S & R всего файла, только последняя строка.
  • 2
    Виноват. Требуется% (что означает «для всех строк») перед s. Исправлена.
28

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

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 разных шаблонов поиска, которые работают только в одном цикле разбора!

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

  • 11
    В вашем (2) - вы можете сделать sed -e "s/$x/$y/" , и это сработает. Не двойные кавычки. Это может серьезно сбить с толку, если строки в самих переменных содержат символы со специальным значением. Например, если x = "/" или x = "\". Когда вы сталкиваетесь с этими проблемами, это, вероятно, означает, что вы должны прекратить попытки использовать оболочку для этой работы.
  • 0
    Привет стройная, я вижу, что ты тоже против использования Perl. Каково ваше решение? Потому что на самом деле я хочу динамически изменить путь в файле, а это значит, что у меня много / в строке!
Показать ещё 1 комментарий
13

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

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

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

  • 7
    Строго говоря, одиночные кавычки, которые вообще не требуют цитирования, в лучшем случае сомнительны стилистически. Использование одной пары двойных кавычек по всему сценарию улучшило бы удобочитаемость и, надеюсь, понимание.
11

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

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

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

  • 2
    Это решение не зависит от несовместимости GNU / FreeBSD (Mac OSX) (в отличие от sed -i <pattern> <filename> ). Очень хорошо!
9

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

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

Используйте я для случая игнорирования, если вы не уверены, что текст для поиска - 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' {} \;
6

Будьте осторожны, если вы замените 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

2

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

  • 1
    Это отличный ответ на вопрос «как случайно все файлы во всех подкаталогах тоже», но, похоже, это не то, что здесь спрашивают.
2

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

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

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

Это эквивалентно @slim answer редактора ex, который в основном то же самое.

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

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

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

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

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

См. также:

1

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

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

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

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

  • 0
    Любые советы по использованию этого с символами табуляции? По какой-то причине мой скрипт не находит ничего с вкладками после перехода с sed с большим количеством экранирования к этому методу.
  • 1
    Если вы хотите поместить вкладку в заменяемую строку, вы можете сделать это с помощью синтаксиса Bash «dollared одинарные кавычки», так что вкладка представляется как $ '\ t', и вы можете сделать $ echo 'tab' $ '\ t''separated'> testfile; $ file_contents = $ (<testfile); $ echo "$ {file_contents // $ '\ t' / TAB}"; tabTABseparated `
1

Вы также можете использовать 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()
1

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню