Вопрос или проблема
Я знаю, что envsubst
заменяет объявленные переменные окружения во входных данных.
$ echo 'Hello $USER' | envsubst
Hello myusername
Что я хочу, так это способ заменить переменную окружения, если она существует, в противном случае envsubst
(или другая команда) оставляет строку переменной как есть. Что я получаю, это:
$ echo 'Hello $USER $UNDEFINED_VARIABLE' | envsubst
Hello myusername
Что я хочу, это:
$ echo 'Hello $USER $UNDEFINED_VARIABLE' | somecommand
Hello myusername $UNDEFINED_VARIABLE
Вы можете использовать env
, чтобы увидеть все текущие определенные переменные окружения, а затем использовать этот список, чтобы заменить только их. (Страница man не очень ясна по этому поводу, но смотрите это ответ для разъяснения.)
echo 'Hello $USER $UNKNOWN' | envsubst "$(env | cut -d= -f1 | sed -e 's/^/$/')"
(Вывод env
также перечисляет значения переменных, но envsubst
также хочет видеть ведущий $
, так что мы не можем просто использовать cut -d= -f1
в одиночку, к сожалению. Вы можете использовать один sed
, чтобы сделать работу cut
, смотрите предыдущее исправление, но я предпочитаю ясность cut
перед небольшой прибавкой производительности.)
Если вы передадите аргумент, такой как $USER$PATH
, в envsubst
, то он расширяет только те переменные, которые упоминаются в этом аргументе.
Так что один из способов может быть передать все текущие определенные переменные окружения в этом формате. С zsh
:
echo 'Hello $USER ${USER} $UNDEFINED_VARIABLE' |
envsubst \$${(kj:$:)parameters[(R)*export*]}
$parameters
— это специальный ассоциативный массив, который сопоставляет имена переменных с их типом$parameters[(R)*export*]
расширяется до всех элементов ассоциативного массива, чье значение содержитexport
.- с параметром
k
для расширения возвращается ключ, а не значение j:$:
соединяет эти элементы с$
между ними, и мы добавляем один в начале.
С другими оболочками вы всегда можете обратиться к perl
, чтобы получить этот список:
echo 'Hello $USER ${USER} $UNDEFINED_VARIABLE' |
envsubst "$(perl -e 'print "\$$_" for grep /^[_a-zA-Z]\w*$/, keys %ENV')"
Осторожно, оба раскрывают имена ваших переменных окружения в выводе ps
.
Вместо этого вы также можете сделать все это в perl
:
perl -pe 's{(?|\$\{([_a-zA-Z]\w*)\}|\$([_a-zA-Z]\w*))}{$ENV{$1}//$&}ge'
Будьте осторожны, у него есть те же ограничения, что и у envsubst
, в том, что он не расширит такие вещи, как ${VAR:-x}
и расширит $HOME
во таких вещах, как \$HOME
или $$HOME
, чего оболочка не сделает.
Чтобы дополнить ответ Ульриха Шварца, предоставляя решение, которое также должно работать для “особых” типов переменных окружения, я использую declare -xp
для получения переменных окружения, как описано здесь:
$ echo 'Hello $USER $UNDEFINED_VARIABLE' | envsubst "$(declare -xp | cut -d= -f1 | awk '{ sub(/^declare -x /, ""); print }' | awk '{ print "$" $0 }' | awk '{ printf("%s ", $0) }')"
Hello <user> $UNDEFINED_VARIABLE
В скрипте:
environment_variables_single_line="$(declare -xp | cut -d= -f1 | awk '{ sub(/^declare -x /, ""); print }' | awk '{ print "$" $0 }' | awk '{ printf("%s ", $0) }')"
envsubst "${environment_variables_in_single_line}" < ${source_file} > ${target_file}
Ответ или решение
Для решения задачи замены переменных среды в тексте, если они существуют, и оставления строк без изменений для незаданных переменных, могут быть использованы различные подходы. В этой статье рассматриваются несколько методов, включая использование инструментов командной строки, таких как envsubst
, perl
и других средств для обработки окружения в Linux.
Метод 1: Использование envsubst
с помощью списков переменных
Одним из самых распространенных инструментов для подстановки переменных является envsubst
. Однако для реализации ваших требований необходимо уточнить, какие переменные подставлять. Это можно сделать, собирая список определенных переменных окружения с помощью команды env
.
Шаги:
-
Соберите все переменные окружения:
ENV_VARS=$(env | cut -d= -f1 | sed 's/^/$/')
-
Используйте
envsubst
с переданным списком переменных:echo 'Hello $USER $UNDEFINED_VARIABLE' | envsubst "$ENV_VARS"
Этот подход заменит существующие переменные и оставит незаданные переменные без изменений, как вы и желаете.
Метод 2: Использование perl
для более сложной обработки
Если требуется более тонкая настройка, можно использовать perl
. Этот язык позволяет работать с регулярными выражениями и предоставляет более мощные возможности.
Пример:
echo 'Hello $USER ${USER} $UNDEFINED_VARIABLE' | perl -pe 's{(\$\{([_a-zA-Z]\w*)\}|\$([_a-zA-Z]\w*))}{$ENV{$2}//$&}ge'
В этом примере если переменная существует в окружении, она будет подставлена, в противном случае оригинальная строка останется.
Метод 3: Использование declare -xp
для получения всех переменных
Еще один подход — использовать declare -xp
, чтобы извлечь список переменных, прежде чем использовать envsubst
. Этот метод подходит, если вы работаете в bash
.
Пример реализации:
ENV_VARS=$(declare -xp | cut -d= -f1 | sed 's/^declare -x /$/' | xargs)
echo 'Hello $USER $UNDEFINED_VARIABLE' | envsubst "$ENV_VARS"
Эта команда получает все экспортируемые переменные и передает их в envsubst
, обеспечивая корректную замену.
Важные заметки
- Используемые методы могут раскрывать имена переменных окружения в процессе выполнения, что может не всегда быть желательным в условиях повышенной безопасности.
- Убедитесь, что переменные окружения настроены должным образом и содержат ожидаемые значения перед выполнением команд.
Заключение
Таким образом, используя инструменты командной строки и языки программирования, такие как perl
, можно эффективно управлять заменой переменных окружения в тексте. Выбор нужного метода будет зависеть от ваших конкретных требований и окружения, в котором вы работаете. Каждый из предложенных подходов позволяет достичь цели, сохраняя нужный уровень гибкости и контроля.