Заміна змінної середовища в 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змінну середовища, як це визначено в оболонці?


4
$ PWD містить a /, що закінчує команду-заміну.
дероберт

@derobert: tnx. Одне з рішень стосується цього ...
РоманМ

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
У вас може бути декілька кандидатів, таких як @ #%! і перевірте з виразом регістру, щоб виявити, чи є в $ PWD. Наприклад, регістр "$ PWD" від @ ) ;; *) delim = "@" ;; есак; повторюйте, поки $ delim не буде порожнім.
Норман Рамзі,

Ще одна альтернатива замість використання подвійних лапок. Дивіться мою відповідь нижче.
Фалес Сеолін

Можна використовувати інший роздільник для sed. Вам не потрібно використовувати / ви можете використовувати, також якщо ваша змінна середовище є URL-адресою.
Гучелкабен

123

На додаток до відповіді Нормана Рамзі, я хотів би додати, що ви можете подвоїти цитування всього рядка (що може зробити заяву більш читаною і менш схильною до помилок).

Тож якщо ви хочете шукати "foo" і замінити його вмістом $ BAR, ви можете укласти команду sed у подвійні лапки.

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

У першому $ BAR не розшириться правильно, тоді як у другому $ BAR буде розгорнутось правильно.


4
Це чистіше, ніж возитися з подвійними цитатами, одинарними цитатами тощо.
Владислав Довгалеч

це те, що мені довелося використати, щоб змінна середовища розширилася правильно в цій команді: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" господарі
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>

3

Насправді, найпростіша річ (як мінімум у gnu sed) - використовувати інший роздільник для команди sed (s). Тож замість s / pattern / '$ mypath' / буде розширено на s / pattern // my / path /, що, звичайно, буде плутати команду s, використовуйте s! Pattern! '$ Mypath'! який буде розширено до s! шаблону! / my / path! Я використовував символ (!) Чуб (або використовувати все, що вам подобається), що дозволяє уникнути звичайного, але відсутнього значення - вашого єдиного нахилу вперед в якості роздільника.


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

де VAR містить те, на що потрібно замінити поле


3

Справа з VARIABLES в межах 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

Я намагався, щоб це працювало в трубопроводах YAML Azure DevOps. Цей коментар допоміг мені успішно використовувати цей трюк для токенізації деяких файлів конфігурації.
andov

1

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

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.