2009-02-08 11 views
318

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

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

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

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

ответ

555

Самый простой способ заключается в использовании СЭД (или Perl):

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

Что будет вызывать СЕПГ, чтобы сделать на месте правку из-за опции -i. Это можно вызвать из bash.

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

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

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

+2

За исключением того, что вызов mv в значительной степени похож на «non Bash» как на использование sed. Я почти сказал то же самое о эхо, но это встроенная оболочка. – slim

+10

Я знаю, что это немного позже, но для других гуглеров: я не думаю, что вы хотите, чтобы там было, если вы не хотите, чтобы ваши оригиналы с дополнительным e в конце. Вы можете оставить это, -op аргумент аргумента будет интерпретироваться как сценарий или сделать '-i -e' – Adam

+5

Однако аргумент -i для sed не существует для Solaris (и я думаю, некоторые другие реализации), поэтому имейте это в виду. Просто потратил несколько минут на то, чтобы понять, что ... – Panky

144

манипуляция Файл обычно не выполняется Bash, а программы, на которые ссылается Bash, например .:

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

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

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

+29

Пурист во мне говорит, что вы не можете быть уверены, что Perl будет доступен в системе. Но это очень редко. Возможно, я показываю свой возраст. – slim

+2

Можете ли вы показать более сложный пример. Что-то вроде замены «chdir/blah» на «chdir/blah2». Я попробовал 'perl -pi -e 's/chdir (?: \\/[\\ w \\. \\ -] +) +/chdir blah/g' text', но я продолжаю получать сообщение об ошибке пространство между рисунком и следующим словом устарело на -e строке 1. Непревзойденный (в регулярном выражении; помечен <- ЗДЕСЬ в m/(chdir)() (<- ЗДЕСЬ?: \\/at -e строка 1. – CMCDragonkai

+0

@slim, ха-ха, действительно, Perl сейчас 27 лет. Я не думаю, что это скоро исчезнет. – Pretzel

34

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

В этом случае есть пара способы сделать это.

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

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

Если вы хотите редактировать файл на месте (как в случае открытия файл в редакторе, редактировать его, а затем сохранить его) инструкцию питания к редактору линии «экс»

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

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

+2

Пример «ex» является опрятным, но не для S & R на весь файл, только Последняя строка. – Alnitak

+2

Мне плохо. Для этого нужно было% (что означает «для всех строк»). Исправлено. – slim

11

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

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

Смотрите больше на bash-hackers site

+2

Это решение несовместимо с несовместимостью GNU/FreeBSD (Mac OSX) (в отличие от' sed -i '). Очень хорошо! – Peterino

28

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

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

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

2) когда мое ограничение использовать его с помощью скрипта, то, никакая переменная не может быть использована внутри вместо аЬс или XYZ! This, похоже, согласен с тем, что я понимаю, по крайней мере. Таким образом, я не могу использовать:

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

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

+9

В вашем (2) - вы можете сделать 'sed -e 's/$ x/$ y /" ', и он будет работать. Не двойные кавычки.Это может серьезно запутать, если строки в самих переменных содержат символы со специальным значением. Например, если x = "/" или x = "\". Когда вы сталкиваетесь с этими проблемами, вероятно, вы должны перестать пытаться использовать оболочку для этой работы. – slim

+0

Привет, я вижу, что вы также против использования Perl. Каково ваше решение? Потому что я на самом деле хочу динамически изменять путь в файле, что означает, что у меня много/в строке! – Mahdi

54

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

Существует «заменить» команда, которая поставляется вместе с пакетом «MySQL-сервер», так что если вы установили его попробовать :

# 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" 

См человек заменить подробнее об этом ...

+0

хорошее открытие, может пригодиться когда-нибудь – Raiyan

+1

Это невероятно полезно. Я использую это прямо сейчас, чтобы создать сценарий установки L4 BASH. – enchance

+7

Возможны две вещи: a) 'replace' - полезный независимый инструмент, и люди MySQL должны выпускать его отдельно и зависеть от него. b)' replace' требует некоторую бит MySQL o_O В любом случае, установка mysql-сервера получить замену было бы неправильно делать :) –

1

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

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

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

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 

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

16

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

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

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

+1

Поверьте мне, это лучший ответ –

+3

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

+0

Лучший ответ до сих пор. – AnirbanDebnath

1

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

#!/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() 
2

находят ./ -type е -name «файл * .txt» | xargs СЕПГ -i -e 's/ABC/XYZ/г'

+1

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

9

Вы можете использовать СЭД

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

Использование я для игнорирования регистра, если Вы не уверены, что текст, чтобы найти это абв или ABC или AbC,. ..

Вы можете использовать найти и СЭД, если вы сейчас не ваше имя файла:

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

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

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \; 
3

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

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

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

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

+0

Любые советы по использованию этого с символами табуляции? По какой-то причине мой скрипт не находит ничего с вкладками после перехода от sed с большим количеством экранов к этому методу. –

+1

Если вы хотите поместить вкладку в заменяемую строку, вы можете сделать это с помощью синтаксиса «купированные одиночные кавычки» Bash, так что вкладка представлена ​​$ '\ t', и вы можете сделать $ echo 'tab '$' \ t''separated '> testfile; $ file_contents = $ ( johnraff