Как я могу использовать переменные в LHS и RHS для замены sed?

61

Я хочу делать:

cat update_via_sed.sh | sed 's/old_name/new_name/' > new_update_via_sed.sh

в моей программе.

Но я хочу использовать переменные, например,

old_run='old_name_952'
new_run='old_name_953'

Я пытался их использовать, но замена не происходит (без ошибок). Я пытался:

cat update_via_sed.sh | sed 's/old_run/new_run/'
cat update_via_sed.sh | sed 's/$old_run/$new_run/'
cat update_via_sed.sh | sed 's/${old_run}/${new_run}/'
Майкл Даррант
источник
2
Вы можете найти ответ в Использовать параметр в аргументе команды .
Манатворк

Ответы:

45

Вы могли бы сделать:

sed "s/$old_run/$new_run/" < infile > outfile

Но будьте осторожны, $old_runэто будет восприниматься как регулярное выражение и любые специальные символы, содержащиеся в переменной, такие как /или .должны быть экранированы . Точно так же, в $new_run, &и \символы и должны быть обработаны специально, и вам придется экранировать /символы и новой строки в нем.

Если содержимое $old_runили $new_runвы не находитесь под вашим контролем, тогда крайне важно выполнить это экранирование или иным образом этот код равносильно уязвимости внедрения кода .

Стефан Шазелас
источник
3
Я использовал одинарные кавычки в Sed Params, переключился на двойные кавычки, и это работало файл. Потрясающие.
Аакаш
не правда ли, что sedодин не будет использовать регулярные выражения, но sed -Eбудет?
Киви
@ Киви, нет. -eчтобы указать sed электронную XPression , так что вы можете передать более одного (как в sed -e exp1 -e exp2) или комбинации файлов и выражений ( sed -f file -e exp). Возможно, вы -Eпутаетесь с тем, что с некоторыми реализациями, говорит sed использовать ERE вместо BRE.
Стефан Шазелас
Я набрал опечатку, но хорошо, это объясняет, почему у меня так много проблем с использованием sed без -Eменя, я только осваиваю расширенное регулярное выражение, а не обычное. Спасибо за объяснение.
Киви
@Kiwy, смотрите также связанные вопросы и ответы для получения дополнительных опций, поддерживаемых некоторыми sedреализациями для еще большего количества синтаксисов регулярных выражений.
Стефан Шазелас
31

Это сработало:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/'

Поскольку я хочу «повторно использовать» один и тот же файл, я фактически использую это для всех, кто желает подобного подхода:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/' > new_update; mv -f new_update update_via_sed.sh

Выше созданный новый файл затем удаляет текущий файл, а затем переименовывает новый файл в текущий файл.

Майкл Даррант
источник
4
Вам не нужны cat, mv или extra '', если вы не собираетесь использовать специальные символы. Так и есть sed -i s/"$old"/"$new"/ update_via_sed.sh. Однако имейте в виду, что это не работает для любого значения старого и нового. например, он не должен содержать / и т. д., поскольку $ old по-прежнему является поиском, а $ new - шаблоном замены, где некоторые символы имеют специальные значения.
frostschutz
1
sed -i "s/$old/$new/"тоже хорошо (с вышеуказанными мерами предосторожности). sedобычно считает разделитель первым символом после sкоманды - то есть, если ваши переменные содержат косую черту, /но не позволяют, скажем, at ( @), вы можете использовать "s @ $ old @ $ new @".
Петер
22

Вы можете использовать переменные в sed, если они заключены в двойные кавычки (а не в одинарные):

sed "s/$var/r_str/g" file_name >new_file

Если /в переменной есть косая черта ( ), используйте другой разделитель, как показано ниже

sed "s|$var|r_str|g" file_name >new_file
мани
источник
1
+1 за упоминание необходимости использовать двойные кавычки. Это была моя проблема.
пятнистый
1
Именно то, что я искал, включая использование |как в моем случае, переменных содержит путь, поэтому он имеет /в нем
йорк
По некоторым причинам труба |работала у меня на MacOS 10.14, но другие разделители любят @или #нет. У меня были косые черты в моем env var тоже.
Давиденке
21

'in-place' sed (используя флаг -i) был ответом. Спасибо Петерф.

sed -i "s@$old@$new@" file
Майкл Даррант
источник
2
У вас есть цитаты в неправильном месте. кавычки должны быть вокруг переменных, а не s@(что не является особенным для оболочки). Лучше всего поместить все в кавычки, как sed -i "s@$old@$new@" file. Обратите внимание, что -iэто не стандартная sedопция.
Стефан Шазелас
как я могу сбежать $в этой команде. Мол, я собираюсь изменить $xxxв целом значение переменной $JAVA_HOME. Я пытался sed -i "s@\$xxx@$JAVA_HOME@" file, но ошибка говоритno previous regular expression
Hakunami
3

man bash дает это о одиночном цитировании

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

Что бы вы ни вводили в командной строке, bash интерпретирует его, а затем отправляет результат в программу, которой он должен быть отправлен. В этом случае, если вы используете sed 's/$old_run/$new_run/', bash сначала видит sed, он распознает его как исполняемый файл, присутствующий в $PATHпеременной. , sedИсполняемый файл требует ввода. Bash ищет вход и находит 's/$old_run/$new_run/'. В одинарных кавычках говорится, что bash не интерпретирует содержимое и передает их такими, какие они есть. Итак, bash затем передает их в sed. Sed выдает ошибку, потому что $может произойти только в конце строки.

Вместо этого, если мы используем двойные кавычки, т. Е., "s/$old_run/$new_run/"Тогда bash видит это и интерпретирует $old_runкак имя переменной и производит подстановку (эта фаза называется расширением переменной). Это действительно то, что нам нужно.

Но вы должны быть осторожны, используя двойные кавычки, потому что они сначала интерпретируются bash, а затем передаются sed. Таким образом, некоторые символы, такие как `, должны быть экранированы перед их использованием.

nitishch
источник
1

На риск быть легкомысленным - вы рассмотрели perl.

Который, помимо прочего, неплохо справляется с операциями 'sed style', но также и с более полнофункциональным программированием.

В вашем примере похоже , что вы на самом деле пытаетесь увеличить число.

Так как насчет:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

while ( <DATA> ) {
    s/old_name_(\d+)/"old_name_".($1+1)/e;
    print;
}

__DATA__
old_name_932

или как один лайнер - стиль седа:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'
Sobrique
источник
1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

В $d1Я хранение значений

Я не могу заменить значение из переменной, поэтому я попробовал следующую команду, она работает

echo $d1 | sed -i 's/zkserver1/'"${d1}"'/g' deva_file

пропущена двойная кавычка "${d1}", это была ошибка

дэва
источник
-2

Предполагается $old_runи $new_runуже установлены:

cat update_via_sed.sh | eval $(print "sed 's/$old_run/$new_run/g'")

Это показывает изменение на экране. При необходимости вы можете перенаправить вывод в файл.

Орландо
источник
-2

Для использования переменной в sed используйте двойные кавычки "", чтобы заключить переменную в шаблон, который вы используете. Например: a = "Hi" Если вы хотите добавить переменную 'a' к шаблону, вы можете использовать следующее: sed -n "1, / pattern" $ {a} "pattern / p" filename

Sher
источник
2
Расширение здесь не заключено $aв кавычки, что означает, что любые пробелы в его значении будут вызывать разбиение слов в оболочке.
Кусалананда
-2

Вы также можете использовать Perl:

perl -pi -e ""s/$val1/$val2/g"" file
самба
источник
Рассмотрим переменные, которые могут иметь пробелы в своих значениях. Пустые строки вокруг выражения Perl ( "") ничего не сделают.
Кусалананда
-2

сломать строку

bad => linux видит строку $ old_run:

sed 's/$old_run/$new_run/'

good => linux видит переменную $ old_run:

sed 's/'$old_run'/'$new_run'/'
marcdahan
источник
1
А если $old_runили $new_runсодержит пробелы?
Кусалананда