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


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". (Поскольку в противном случае перенаправление оболочки очистит файл перед его чтением.)


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 справился бы лучше, у вас есть доступ к переменным среды в виде массива, и вы можете выполнять замену исполняемых файлов, чтобы сопоставить любую переменную среды только один раз.


почему кто-то отрицает такой ответ ??? -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

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


В самом деле, это риск при выполнении «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"

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.