Подстановка переменной среды в sed

202

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

#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переменную окружения, как она определена в оболочке?

RomanM
источник
4
$ PWD содержит /, заканчивающий команду замены.
Дероберт
@derobert: tnx. Одно из решений направлено на это ...
RomanM
4
Используйте set -xв оболочке, чтобы оболочка отображала каждую команду непосредственно перед ее выполнением. Это может устранить много путаницы. (Кроме того, я часто использую, set -uчтобы сделать set -e
отмену
Я надеялся найти способ для sed обрабатывать переменные среды, чтобы не пропускать значения в таблицу процессов, похоже, что sed - это неправильный инструмент для установки секретов в соответствии со всеми ответами в этой теме
ThorSummoner

Ответы:

302

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

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

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

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

sed 's@xxx@'"$PWD"'@'
Норман Рэмси
источник
но тогда какой символ я могу использовать, я точно знаю, не появится в пути?
RomanM
5
Вы можете иметь несколько кандидатов, таких как @ #%! и проверьте с помощью выражения case, чтобы найти, есть ли у $ PWD их. Например, регистр "$ PWD" из @ ) ;; *) delim = "@" ;; ESAC; повторяйте, пока $ delim не будет пустым.
Норман Рэмси
Есть другая альтернатива вместо использования двойных кавычек. Смотрите мой ответ ниже.
Фалес Цеолин
Вы можете использовать другой разделитель для sed. Вам не нужно использовать / вы можете использовать, если ваша переменная окружения является URL-адресом.
Guchelkaben
123

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

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

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

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

Jeach
источник
4
Это чище, чем возиться с двойными кавычками, одинарными кавычками и т. Д.
Владислав Довгальец
это то, что я должен был использовать для правильного расширения переменной среды в этой команде: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw
2
Это аккуратное , но он не работает , когда вы хотите сделать некоторые более сложные замены, таким как , "2,$s/^/$foo/"как $sполучает истолкована как переменные тоже , и он не должен.
mjp
59

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

sed "s#$1#$2#g" -i FILE
Пауло Фидальго
источник
В моем конкретном случае, $ 2 был путь к файлу, так что sedбыло недоразумением из-за интерпретации /в $ 2. Это было именно то, что мне нужно, чтобы пройти через это. Спасибо за отличный совет!
Бойд Хемфилл
54

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

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

sed -e "s|xxx|$PWD|"
Фалес Цеолин
источник
4
Вы сказали «альтернатива двойным кавычкам», и все же ваш пример использует двойные кавычки?
Jeach
Я думаю, дело в том, что он не использует двойные кавычки прямо вокруг $PWD...?
Кристиан
14

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

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

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

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

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

Эдди
источник
но PWD определяется оболочкой ... если я пойду на эхо $ PWD, я получу pwd
RomanM
1
Итак, как вы избегаете переменных среды?
einpoklum
1
Выбранный ответ описывает обходной путь ... не используйте косую черту в качестве разделителя
Эдди
Что станет проблемой, как только ваш текущий рабочий каталог будет содержать этот другой символ.
blubberdiblub
6

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

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

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

Вы не можете известно , какой символ будет происходить $PWD, / :OR @.

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

хороший способ: экранирующий символ

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

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

в строке $ tmp

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

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

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

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

echo "${url//\//\/}"
x.com:80\/aa\/bb\/aa.js

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>

ИЛИ

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

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

echo "${url//:/\:}"
x.com\:80/aa/bb/aa.js

echo $tmp | sed "s:URL:${url//:/\:}:g"
<a href="x.com:80/aa/bb/aa.js">x.com:80/aa/bb/aa.js</a>
yurenchen
источник
3

На самом деле, самая простая вещь (по крайней мере в gnu sed) - это использовать другой разделитель для команды sed substitution (s). Поэтому вместо того, чтобы s / pattern / '$ mypath' / был расширен до s / pattern // my / path /, что, конечно же, запутает команду s, используйте s! Pattern! '$ Mypath'! который будет расширен до s! pattern! / my / path! Я использовал символ взрыва (!) (Или используйте все, что вам нравится), который избегает обычной косой черты «только из-за вашего выбора» в качестве разделителя.

adeger
источник
3
VAR=8675309
echo "abcde:jhdfj$jhbsfiy/.hghi$jh:12345:dgve::" |\
sed 's/:[0-9]*:/:'$VAR':/1' 

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

PsychoData
источник
3

Работа с переменными в Сед

[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
Мамаду Ламине Диатта
источник
Я изо всех сил пытался заставить это работать в конвейерах YAML Azure DevOps. Этот комментарий помог мне успешно использовать этот трюк для токенизации некоторых файлов конфигурации.
Андов
1

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

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
aniskop
источник