Замените переменные окружения в тексте, если они существуют.

Вопрос или проблема

Я знаю, что 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.

Шаги:

  1. Соберите все переменные окружения:

    ENV_VARS=$(env | cut -d= -f1 | sed 's/^/$/')
  2. Используйте 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, можно эффективно управлять заменой переменных окружения в тексте. Выбор нужного метода будет зависеть от ваших конкретных требований и окружения, в котором вы работаете. Каждый из предложенных подходов позволяет достичь цели, сохраняя нужный уровень гибкости и контроля.

Оцените материал
Добавить комментарий

Капча загружается...