Как заменить переменные оболочки в сложных текстовых файлах

89

У меня есть несколько текстовых файлов, в которых я ввел переменные оболочки (например, $ VAR1 или $ VAR2).

Я хотел бы взять эти файлы (один за другим) и сохранить их в новых файлах, где все переменные были бы заменены.

Для этого я использовал следующий сценарий оболочки (можно найти на StackOverflow):

while read line
do
    eval echo "$line" >> destination.txt
done < "source.txt"

Это очень хорошо работает с очень простыми файлами.

Но для более сложных файлов команда eval делает слишком много:

  • Строки, начинающиеся с "#", пропускаются

  • Анализ XML-файлов приводит к множеству ошибок

Есть ли способ лучше? (в сценарии оболочки ... я знаю, что это легко сделать, например, с помощью Ant)

С уважением

Бен
источник

Ответы:

205

Глядя, оказывается, что в моей системе есть envsubstкоманда, которая является частью пакета gettext-base.

Итак, это упрощает:

envsubst < "source.txt" > "destination.txt"

Обратите внимание , если вы хотите использовать один и тот же файл для обоих, вам придется использовать что - то вроде moreutil - х sponge, как предложил Джонни Utahh: envsubst < "source.txt" | sponge "source.txt". (Поскольку в противном случае перенаправление оболочки очистит файл перед его чтением.)

Derobert
источник
4
Большой! Но это работает только для переменных среды. Как я могу заставить его работать с переменными, объявленными в моем сценарии .sh?
Бен
12
@Ben: используйте 'export' (перед вызовом envsubst) для каждой переменной, которую вы хотите использовать в envsubst
tlo
9
envsubstявляется частью GNU gettext
Энди
3
@user_mda, куда идет вывод.
derobert
4
Предупреждение: если он source.txtсодержит какие-либо $символы вне раскрытия переменной, envsubstони также будут заменены, и нет возможности избежать $. Обычный (уродливый) обходной путь - export DOLLAR="$".
Кос
67

Что касается ответа 2, при обсуждении envsubst вы спросили:

Как я могу заставить его работать с переменными, объявленными в моем сценарии .sh?

Ответ: вам просто нужно экспортировать свои переменные перед вызовом envsubst.

Вы также можете ограничить переменные строки, которые вы хотите заменить во входных данных, используя envsubst SHELL_FORMATаргумент (избегая непреднамеренной замены строки во входных данных общим значением переменной оболочки - например $HOME).

Например:

export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'

envsubst "$MYVARS" <source.txt >destination.txt

Заменим все экземпляры $VAR1и $VAR2(и только VAR1и VAR2) в source.txtс 'somevalue'и 'someothervalue'соответственно.

Майкл
источник
15
Одиночные кавычки ', которые используются для установки, MYVARSимеют решающее значение
Марк Лаката 02 дек.15,
Обратите внимание, что export, envsubstили любая команда чередования может завершиться ошибкой, если текст для замены большой. Справка.
епископ
@ram это потому, что отсутствие SHELL_FORMATаргумента (т.е. "$MYVARS") приводит к замене всех экспортируемых переменных. Если это то, что вам нужно, не беспокойтесь.
Тьяго Фигейро
14

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

var1='myVar1'\
var2=2\
var3=${var1}\
envsubst '$var1,$var3' < "source.txt" > "destination.txt"

#         ^^^^^^^^^^^    ^^^^^^^^^^     ^^^^^^^^^^^^^^^
# define which to replace   input            output

Переменные должны быть определены в той же строке, что и envsubstпеременные среды.

'$var1,$var3'Не является обязательным только заменить указанные оговорки. Представьте себе входной файл, ${VARIABLE_USED_BY_JENKINS}который не следует заменять.

инетфант
источник
8
  1. Определите вашу переменную ENV
$ export MY_ENV_VAR=congratulation
  1. Создайте файл шаблона ( in.txt ) со следующим содержимым
$MY_ENV_VAR

Вы также можете использовать все другие переменные ENV, определенные вашей системой, например (в linux) $ TERM, $ SHELL, $ HOME ...

  1. Запустите эту команду, чтобы заменить все переменные env в файле in.txt и записать результат в out.txt
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
  1. Проверьте содержимое файла out.txt
$ cat out.txt

и вы должны увидеть «поздравление».

Дадо
источник
0

Если вы действительно хотите использовать только bash (и sed), я бы прошел через каждую из ваших переменных среды (как возвращено setв режиме posix) и построил кучу -e 'regex'для sed из этого, завершившегося a -e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g', а затем передал все это в sed.

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

w00t
источник
почему кто-то отрицает такой ответ ??? -perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' "$main_tf_file"
Йордан Георгиев
0

На самом деле вам нужно изменить ваш readна read -rкоторый заставит его проигнорировать обратные косую черту.

Кроме того, вам следует избегать кавычек и обратных косых черт. Так

while read -r line; do
  line="${line//\\/\\\\}"
  line="${line//\"/\\\"}"
  line="${line//\`/\\\`}"
  eval echo "\"$line\""
done > destination.txt < source.txt

Тем не менее, это все еще ужасный способ расширения.

w00t
источник
В самом деле, это риск при выполнении «eval» для файла, который может содержать плохой код
Бен
0

Экспортируйте все необходимые переменные, а затем используйте perl onliner

TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $& #ge;')

Это заменит все переменные ENV, присутствующие в TEXT, на фактические значения. Цитаты тоже сохранились :)

Говинд Кайлас
источник
0

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

envsubst "$(printf '${%s} ' $(env | sed 's/=.*//'))" < source.txt > destination.txt

Здесь объясняется синтаксис для замены только определенных переменных . Приведенная выше команда использует вспомогательную оболочку для перечисления всех определенных переменных и затем передает ее вenvsubst

Итак, если есть определенная переменная env $NAME, и ваш source.txtфайл выглядит так:

Hello $NAME
Your balance is 123 ($USD)

destination.txtБудет:

Hello Arik
Your balance is 123 ($USD)

Обратите внимание, что $NAMEзаменяется, а $USDостается нетронутым

Арик
источник
0

Вызовите двоичный файл perl в режиме поиска и замены для каждой строки ( -pi), запустив код perl ( -e) в одинарных кавычках, который выполняет итерацию по ключам специального %ENVхэша, содержащего имена экспортируемых переменных в качестве ключей и значения экспортируемых переменных в виде значения ключей и для каждой итерации просто замените строку, содержащую a, $<<key>>на ее <<value>>.

 perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file

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

Йордан Георгиев
источник
-1

envsubstпохоже, именно то, что я хотел использовать, но -vвариант меня немного удивил.

Пока envsubst < template.txtработал нормально, то же самое с опцией -vне работало:

$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.1 (Maipo)
$ envsubst -V
envsubst (GNU gettext-runtime) 0.18.2
Copyright (C) 2003-2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Bruno Haible.

Как я уже писал, это не сработало:

$ envsubst -v < template.txt
envsubst: missing arguments
$ cat template.txt | envsubst -v
envsubst: missing arguments

Мне пришлось сделать это, чтобы он заработал:

TEXT=`cat template.txt`; envsubst -v "$TEXT"

Может кому поможет.

Betlista
источник