Подстановка переменных окружения в sed

Если я запустил эти команды из скрипта:

#my.sh
PWD=bla
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
xxx
bla

это нормально.

Но если я побегу:

#my.sh
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
$ sed: -e expression #1, char 8: Unknown option to `s' 

Я читал в учебных пособиях, что для замены переменных среды из оболочки вам нужно остановить, и "цитата" часть $varname, чтобы она не заменялась напрямую, что я и сделал, и которая работает только в том случае, если переменная определена непосредственно перед этим.

Как я могу заставить sed распознавать $var как переменную среды, как она определена в оболочке?


person RomanM    schedule 25.02.2009    source источник
comment
$ PWD содержит /, который завершает замену команды.   -  person derobert    schedule 25.02.2009
comment
@derobert: TNX. Одно из решений решает эту проблему ...   -  person RomanM    schedule 25.02.2009
comment
Используйте set -x в оболочке, чтобы оболочка отображала каждую команду непосредственно перед их выполнением. Это может устранить большую путаницу. (Кроме того, я часто использую set -u, чтобы сделать разыменование неустановленных переменных серьезной ошибкой. (См. Также set -e.))   -  person bobbogo    schedule 15.08.2012
comment
Я надеялся найти способ для sed обрабатывать переменные среды, чтобы не пропускать значения в таблицу процессов, похоже, что sed - неправильный инструмент для установки секретов согласно всем ответам в этом потоке   -  person ThorSummoner    schedule 10.01.2019


Ответы (10)


Два ваших примера выглядят одинаково, что затрудняет диагностику проблем. Потенциальные проблемы:

  1. Вам могут понадобиться двойные кавычки, как в sed 's/xxx/'"$PWD"'/'

  2. $PWD может содержать косую черту, и в этом случае вам нужно найти символ, не содержащийся в $PWD, чтобы использовать его в качестве разделителя.

Возможно, чтобы решить обе проблемы одновременно

sed 's@xxx@'"$PWD"'@'
person Norman Ramsey    schedule 25.02.2009
comment
но тогда какой символ я могу использовать, я точно знаю, что он не появится в имени пути? - person RomanM; 25.02.2009
comment
У вас может быть несколько кандидатов, например @ #%! и проверьте с помощью выражения case, есть ли они в $ PWD. Например, case $ PWD of @) ;; *) delim = @ ;; esac; повторять, пока $ delim не станет пустым. - person Norman Ramsey; 25.02.2009
comment
Вместо двойных кавычек есть еще одна альтернатива. См. Мой ответ ниже. - person Thales Ceolin; 17.04.2014
comment
Вы можете использовать другой разделитель для sed. Вам не нужно использовать / вы также можете использовать, если ваша переменная среды является URL-адресом. - person Guchelkaben; 05.06.2020
comment
Что, если строка содержит \, за которым следует n - как остановить преобразование sed в один символ новой строки? - person Max Waterman; 24.07.2020

В дополнение к ответу Нормана Рэмси я хотел бы добавить, что вы можете заключить в двойные кавычки всю строку (что может сделать утверждение более читаемым и менее подверженным ошибкам).

Поэтому, если вы хотите найти «foo» и заменить его содержимым $ BAR, вы можете заключить команду sed в двойные кавычки.

sed 's/foo/$BAR/g'
sed "s/foo/$BAR/g"

В первом случае $ BAR не будет правильно расширяться, а во втором $ BAR будет расширяться правильно.

person Jeach    schedule 14.04.2009
comment
Это чище, чем возиться с двойными кавычками, одинарными кавычками и т. Д. - person Vladislavs Dovgalecs; 12.09.2014
comment
это то, что мне пришлось использовать для правильного расширения переменной среды в этой команде: sed -i s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME / hosts - person izikandrw; 05.07.2016
comment
Это удобно, но это не работает, когда вы хотите выполнить более сложные замены, такие как "2,$s/^/$foo/", поскольку $s тоже интерпретируется как переменная, а этого не должно быть. - person mjp; 10.11.2017
comment
Само по себе это не сработало, поскольку мне все еще нужен был другой разделитель для путей, как предлагают другие ответы. - person MoRe; 02.12.2020
comment
@mjp, то, что вы говорите, правильно, но я думаю, что можно написать это как "2,$ s/^/$foo/" - person NameOfTheRose; 28.01.2021

Еще одна простая альтернатива:

Поскольку $PWD обычно содержит косую черту /, используйте | вместо / для оператора sed:

sed -e "s|xxx|$PWD|"
person Thales Ceolin    schedule 17.04.2014
comment
Вы сказали, что есть альтернатива использованию двойных кавычек, но в вашем примере используются двойные кавычки? - person Jeach; 10.12.2014
comment
Я думаю, дело в том, что он не использует двойные кавычки непосредственно вокруг _1 _...? - person Christian; 11.10.2019

Вы можете использовать вместо "/" другие символы:

sed "s#$1#$2#g" -i FILE
person Paulo Fidalgo    schedule 08.03.2012
comment
В моем конкретном случае $ 2 был путем к файлу, поэтому sed отключался из-за интерпретации / в содержимом $ 2. Это было именно то, что мне нужно, чтобы пройти через это. Спасибо за отличный совет! - person Boyd Hemphill; 18.09.2015

С редактированием вашего вопроса я вижу вашу проблему. Скажем, текущий каталог /home/yourname ... в этом случае ваша команда ниже:

sed 's/xxx/'$PWD'/'

будет расширен до

sed `s/xxx//home/yourname//

что неверно. Вам нужно поставить \ символ перед каждым / в вашем $ PWD, если вы хотите это сделать.

person Eddie    schedule 25.02.2009
comment
но PWD определяется оболочкой ... если я иду echo $ PWD, я получаю pwd - person RomanM; 25.02.2009
comment
Итак, как избежать переменных окружения? - person einpoklum; 18.02.2014
comment
Выбранный ответ описывает обходной путь ... не используйте косую черту в качестве разделителя - person Eddie; 19.02.2014
comment
Что станет проблемой, как только ваш текущий рабочий каталог будет содержать этот другой персонаж. - person blubberdiblub; 16.10.2014

一. плохой способ: изменить разделитель

sed 's/xxx/'"$PWD"'/'
sed 's:xxx:'"$PWD"':'
sed 's@xxx@'"$PWD"'@'

может это не окончательный ответ,

вы не можете знать, какой символ будет в $PWD, / : ИЛИ @.

Хороший способ - заменить (избежать) специальный символ в $PWD.

二. хороший способ: escape-разделитель

например: попробуйте заменить URL на $ url (содержит : /)

x.com:80/aa/bb/aa.js

в строке $ tmp

<a href="URL">URL</a>

A. используйте / в качестве разделителя

escape / как \/ в var (перед использованием в выражении sed)

echo ${url//\//\\/}
x.com:80\/aa\/bb\/aa.js   #escape fine

echo ${url//\//\/}
x.com:80/aa/bb/aa.js      #escape not success

echo "${url//\//\/}"
x.com:80\/aa\/bb\/aa.js   #escape fine, notice `"`

echo $tmp | sed "s/URL/${url//\//\\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

echo $tmp | sed "s/URL/${url//\//\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

OR

Б. используйте : в качестве разделителя (более читаемый, чем /)

escape : как \: в var (перед использованием в выражении sed)

echo ${url//:/\:}
x.com:80/aa/bb/aa.js     #escape not success

echo "${url//:/\:}"
x.com\:80/aa/bb/aa.js    #escape fine, notice `"`

echo $tmp | sed "s:URL:${url//:/\:}:g"
<a href="x.com:80/aa/bb/aa.js">x.com:80/aa/bb/aa.js</a>
person yurenchen    schedule 24.08.2017

Работа с ПЕРЕМЕННЫМИ в sed

[root@gislab00207 ldom]# echo domainname: None > /tmp/1.txt

[root@gislab00207 ldom]# cat /tmp/1.txt

domainname: None

[root@gislab00207 ldom]# echo ${DOMAIN_NAME}

dcsw-79-98vm.us.oracle.com

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: ${DOMAIN_NAME}/g'

 --- Below is the result -- very funny.

domainname: ${DOMAIN_NAME}

 --- You need to single quote your variable like this ... 

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: '${DOMAIN_NAME}'/g'


--- The right result is below 

domainname: dcsw-79-98vm.us.oracle.com
person Mamadou Lamine Diatta    schedule 27.08.2013
comment
Я изо всех сил пытался заставить это работать в конвейерах Azure DevOps YAML. Этот комментарий помог мне успешно использовать этот трюк для токенизации некоторых файлов конфигурации. - person andov; 18.02.2020

На самом деле, самое простое (по крайней мере, в GNU sed) - использовать другой разделитель для команды подстановки sed (s). Таким образом, вместо расширения s/pattern/'$mypath'/ до s/pattern//my/path/, что, конечно, запутает команду s, используйте s!pattern!'$mypath'!, который будет расширен до s!pattern!/my/path!. Я использовал символ взрыва (!) (или использую все, что вам нравится), в котором в качестве разделителя не используется обычная косая черта, которая не является вашим единственным выбором.

person adeger    schedule 27.01.2012

VAR=8675309
echo "abcde:jhdfj$jhbsfiy/.hghi$jh:12345:dgve::" |\
sed 's/:[0-9]*:/:'$VAR':/1' 

где VAR содержит то, что вы хотите заменить поле на

person PsychoData    schedule 10.04.2013

У меня была аналогичная проблема, у меня был список, и мне нужно создать SQL-скрипт на основе шаблона (который содержал @INPUT@ в качестве заменяемого элемента):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
person aniskop    schedule 25.03.2013