Шаблоны Bash: как создавать файлы конфигурации из шаблонов с помощью Bash?

134

Я пишу сценарий для автоматизации создания файлов конфигурации для Apache и PHP для моего собственного веб-сервера. Я не хочу использовать графические интерфейсы вроде CPanel или ISPConfig.

У меня есть несколько шаблонов файлов конфигурации Apache и PHP. Скрипту Bash необходимо читать шаблоны, производить подстановку переменных и выводить проанализированные шаблоны в какую-то папку. Каков наилучший способ сделать это? Я могу придумать несколько способов. Какой из них лучший или, может быть, есть еще способы сделать это лучше? Я хочу сделать это в чистом Bash (например, в PHP это просто)

1) Как заменить заполнители $ {} в текстовом файле?

template.txt:

the number is ${i}
the word is ${word}

script.sh:

#!/bin/sh

#set variables
i=1
word="dog"
#read in template one line at the time, and replace variables
#(more natural (and efficient) way, thanks to Jonathan Leffler)
while read line
do
    eval echo "$line"
done < "./template.txt"

Кстати, как мне перенаправить вывод во внешний файл здесь? Нужно ли мне что-то экранировать, если переменные содержат, скажем, кавычки?

2) Использование cat & sed для замены каждой переменной ее значением:

Данный template.txt:

The number is ${i}
The word is ${word}

Команда:

cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${word}/dog/"

Мне это кажется плохим из-за необходимости избегать множества разных символов и со многими переменными строка будет слишком длинной.

Вы можете придумать какое-нибудь другое элегантное и безопасное решение?

Владислав Раструсный
источник
Отвечает ли это на ваш вопрос? Как заменить заполнители $ {} в текстовом файле?
Бен Легжеро

Ответы:

61

Вы можете использовать это:

perl -p -i -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < template.txt

для замены всех ${...}строк соответствующими переменными окружения (не забудьте экспортировать их перед запуском этого скрипта).

Для чистого bash это должно работать (при условии, что переменные не содержат строк $ {...}):

#!/bin/bash
while read -r line ; do
    while [[ "$line" =~ (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) ]] ; do
        LHS=${BASH_REMATCH[1]}
        RHS="$(eval echo "\"$LHS\"")"
        line=${line//$LHS/$RHS}
    done
    echo "$line"
done

, Решение, которое не зависает, если RHS ссылается на некоторую переменную, которая ссылается на себя:

#!/bin/bash
line="$(cat; echo -n a)"
end_offset=${#line}
while [[ "${line:0:$end_offset}" =~ (.*)(\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})(.*) ]] ; do
    PRE="${BASH_REMATCH[1]}"
    POST="${BASH_REMATCH[4]}${line:$end_offset:${#line}}"
    VARNAME="${BASH_REMATCH[3]}"
    eval 'VARVAL="$'$VARNAME'"'
    line="$PRE$VARVAL$POST"
    end_offset=${#PRE}
done
echo -n "${line:0:-1}"

ВНИМАНИЕ : я не знаю, как правильно обрабатывать ввод с помощью NUL в bash или сохранять количество завершающих символов новой строки. Последний вариант представлен как есть, потому что оболочки «любят» двоичный ввод:

  1. read интерпретирует обратную косую черту.
  2. read -r не интерпретирует обратную косую черту, но все равно отбрасывает последнюю строку, если она не заканчивается новой строкой.
  3. "$(…)"будет полоса , как много хвостовых символов новой строки , как там присутствует, так что в конечном с ; echo -n aи использованием echo -n "${line:0:-1}": это капли последнего символа (который a) и сохраняют как много хвостовых символов новой строки , поскольку не было на входе ( в том числе нет).
ZYX
источник
3
Я бы изменил [^}]на [A-Za-Z_][A-Za-z0-9_]в версии bash, чтобы оболочка не выходила за рамки строгой подстановки (например, если она пыталась обработать ${some_unused_var-$(rm -rf $HOME)}).
Крис Джонсен
2
@FractalizeR, возможно, вы захотите изменить $&решение perl на "": сначала оставляет ${...}нетронутым, если он не удается заменить, второй заменяет его пустой строкой.
ZyX
5
ПРИМЕЧАНИЕ. По-видимому, произошло изменение с bash 3.1 на 3.2 (и выше), в котором одинарные кавычки вокруг регулярного выражения - обрабатывают содержимое регулярного выражения как строковый литерал. Таким образом, приведенное выше регулярное выражение должно быть ... (\ $ \ {[a-zA-Z _] [a-zA-Z_0-9] * \}) stackoverflow.com/questions/304864/…
Blue Waters
2
Чтобы whileцикл считывал последнюю строку, даже если она не заканчивается символом новой строки, используйте while read -r line || [[ -n $line ]]; do. Кроме того, ваша readкоманда удаляет начальные и конечные пробелы из каждой строки; чтобы избежать этого, используйтеwhile IFS= read -r line || [[ -n $line ]]; do
mklement0
2
Просто отметим ограничение для тех, кто ищет комплексное решение: эти удобные в остальном решения не позволяют выборочно защищать ссылки на переменные от расширения (например, путем \ -эскапирования).
mklement0
138

Пытаться envsubst

FOO=foo
BAR=bar
export FOO BAR

envsubst <<EOF
FOO is $FOO
BAR is $BAR
EOF
yottatsa
источник
12
Просто для справки, envsubstне требуется при использовании heredoc, поскольку bash рассматривает heredoc как буквальную строку с двойными кавычками и уже интерполирует в нее переменные. Однако это отличный выбор, если вы хотите прочитать шаблон из другого файла. Хорошая замена гораздо более громоздкому m4.
beporter
2
Я был очень приятно удивлен, узнав об этой команде. Я безуспешно пытался настроить функционал envsubst вручную. Спасибо yottatsa!
Тим Стюарт
4
Примечание: envsubstэто утилита GNU gettext, и на самом деле она не так уж и надежна (поскольку gettext предназначена для локализации человеческих сообщений). Что наиболее важно, он не распознает замену $ {VAR} с экранированной обратной косой чертой (поэтому у вас не может быть шаблона, который использует замены $ VAR во время выполнения, например, сценарий оболочки или файл conf Nginx). См. Мой ответ о решении, которое обрабатывает экранирование обратной косой черты.
Стюарт П. Бентли
4
@beporter В этом случае, если вы по какой-то причине захотите передать этот шаблон в envsubst, вы захотите использовать <<"EOF", который не интерполирует переменные (терминаторы в кавычках похожи на одинарные кавычки в heredocs).
Стюарт П. Бентли
2
Я использовал это так: cat template.txt | envsubst
truthadjustr
47

envsubst был для меня новым. Фантастика.

Для записи, использование heredoc - отличный способ создать шаблон для файла conf.

STATUS_URI="/hows-it-goin";  MONITOR_IP="10.10.2.15";

cat >/etc/apache2/conf.d/mod_status.conf <<EOF
<Location ${STATUS_URI}>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from ${MONITOR_IP}
</Location>
EOF
Дэн Гартвейт
источник
1
Я предпочитаю это лучше, чем envsubstпотому, что это спасло меня от apt-get install gettext-baseлишнего в моем Dockerfile
truthadjustr
Оболочка как сценарий, подобный шаблону, однако без установки внешней библиотеки и без стресса от сложных выражений.
千 木 郷
35

Я согласен с использованием sed: это лучший инструмент для поиска / замены. Вот мой подход:

$ cat template.txt
the number is ${i}
the dog's name is ${name}

$ cat replace.sed
s/${i}/5/
s/${name}/Fido/

$ sed -f replace.sed template.txt > out.txt

$ cat out.txt
the number is 5
the dog's name is Fido
Хай вю
источник
1
Для этого нужен временный файл для строки подстановки, верно? Есть ли способ сделать это без временных файлов?
Владислав Раструсный
@FractalizeR: в некоторых версиях sed есть -iопция (редактировать файлы на месте), аналогичная опции perl . Проверьте страницу руководства для вашего sed .
Крис Джонсен
@FractalizeR Да, sed -i заменит inline. Если вам комфортно с Tcl (еще один язык сценариев), посмотрите эту ветку: stackoverflow.com/questions/2818130/…
Хай Ву,
Я создал replace.sed из файлов свойств с помощью следующей команды sed: sed -e 's / ^ / s \ / $ {/ g' -e 's / = /} \ // g' -e 's / $ / \ // g 'the.properties> replace.sed
Jaap D
Код @hai vu создает программу sed и передает эту программу с помощью флага sed -f. При желании вы можете вместо этого передать каждую строку программы sed в sed, используя флаги -e. FWIW Мне нравится идея использовать sed для создания шаблонов.
Эндрю Олбрайт,
23

Я думаю, что eval работает очень хорошо. Он обрабатывает шаблоны с переносами строк, пробелами и всевозможными вещами bash. Если, конечно, вы полностью контролируете сами шаблоны:

$ cat template.txt
variable1 = ${variable1}
variable2 = $variable2
my-ip = \"$(curl -s ifconfig.me)\"

$ echo $variable1
AAA
$ echo $variable2
BBB
$ eval "echo \"$(<template.txt)\"" 2> /dev/null
variable1 = AAA
variable2 = BBB
my-ip = "11.22.33.44"

Разумеется, этот метод следует использовать с осторожностью, поскольку eval может выполнять произвольный код. Запустить это как root практически не может быть и речи. Цитаты в шаблоне нужно экранировать, иначе они будут съедены eval.

Вы можете также использовать здесь документы , если вы предпочитаете , catчтобыecho

$ eval "cat <<< \"$(<template.txt)\"" 2> /dev/null

@plockc предоставил решение, которое позволяет избежать проблемы с экранированием цитаты bash:

$ eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

Изменить: удалена часть о запуске этого как root с помощью sudo ...

Изменить: добавлен комментарий о том, как нужно экранировать кавычки, добавлено решение plockc в микс!

mogsie
источник
Это разделяет кавычки, которые есть в вашем шаблоне, и не заменяет их внутри одинарных кавычек, поэтому в зависимости от формата вашего шаблона это может привести к незначительным ошибкам. Однако это, вероятно, применимо к любому методу создания шаблонов на основе Bash.
Alex B
ИМХО шаблоны на основе Bash - это безумие, поскольку вам нужно быть программистом на bash, чтобы понимать, что делает ваш шаблон! Но спасибо за комментарий!
mogsie
@AlexB: этот подход будет заменять одинарные кавычки, поскольку они являются просто буквальными символами внутри заключенной в двойные кавычки строки, а не разделителями строк, когда команды evaled echo / catобрабатывают их; попробуй eval "echo \"'\$HOME'\"".
mklement0
21

У меня есть решение bash, такое как mogsie, но с heredoc вместо herestring, чтобы вы могли избежать двойных кавычек

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
plockc
источник
4
Это решение поддерживает расширение параметра Bash в шаблоне. Мои фавориты - это обязательные параметры с ${param:?}вложенным текстом вокруг необязательных параметров. Пример: ${DELAY:+<delay>$DELAY</delay>}заменяется на ничто, если DELAY не определено, и <delay> 17 </delay>, когда DELAY = 17.
Эрик Болинджер
2
Ой! И разделитель EOF может использовать динамическую строку, например PID _EOF_$$.
Эрик Болинджер
1
@ mklement0 Обходной путь для завершающих символов новой строки - использовать некоторое расширение, например, пустую переменную $trailing_newline, или использовать $NL5и убедиться, что она расширяется до 5 символов новой строки.
xebeche
@xebeche: Да, размещение того, что вы предлагаете, в самом конце внутри,template.txt будет работать, чтобы сохранить завершающие символы новой строки.
mklement0 06
1
Элегантное решение, но обратите внимание, что подстановка команды удалит все завершающие символы новой строки из входного файла, хотя, как правило, это не проблема. Другой крайний случай: из-за использования eval, если он template.txtсодержится EOFв отдельной строке, он преждевременно завершит работу с файлом here-doc и, таким образом, прервет команду. (Совет от @xebeche).
mklement0 07
18

Редактировать 6 января, 2017

Мне нужно было сохранить двойные кавычки в моем файле конфигурации, поэтому помогает двойное экранирование двойных кавычек с помощью sed:

render_template() {
  eval "echo \"$(sed 's/\"/\\\\"/g' $1)\""
}

Я не могу думать о сохранении новых строк в конце, но пустые строки между ними сохраняются.


Хотя это старая тема, ИМО я нашел здесь более элегантное решение: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/

#!/bin/sh

# render a template configuration file
# expand variables + preserve formatting
render_template() {
  eval "echo \"$(cat $1)\""
}

user="Gregory"
render_template /path/to/template.txt > path/to/configuration_file

Все кредиты Грегори Пакошу .

ЦКК
источник
Это удаляет двойные кавычки из ввода и, если во входном файле есть несколько завершающих символов новой строки, заменяет их одной.
mklement0
2
Мне нужно было на две обратные eval "echo \"$(sed 's/\"/\\"/g' $1)\""
Дэвид Бау
К сожалению, такой подход не позволяет создавать шаблоны php-файлов (они содержат $variables).
IStranger
10

Вместо того, чтобы изобретать колесо, используйте envsubst. Может использоваться практически в любом сценарии, например, для создания файлов конфигурации из переменных среды в контейнерах докеров.

Если на Mac убедитесь, что у вас есть homebrew, свяжите его с помощью gettext:

brew install gettext
brew link --force gettext

./template.cfg

# We put env variables into placeholders here
this_variable_1 = ${SOME_VARIABLE_1}
this_variable_2 = ${SOME_VARIABLE_2}

./.env:

SOME_VARIABLE_1=value_1
SOME_VARIABLE_2=value_2

./configure.sh

#!/bin/bash
cat template.cfg | envsubst > whatever.cfg

Теперь просто используйте это:

# make script executable
chmod +x ./configure.sh
# source your variables
. .env
# export your variables
# In practice you may not have to manually export variables 
# if your solution depends on tools that utilise .env file 
# automatically like pipenv etc. 
export SOME_VARIABLE_1 SOME_VARIABLE_2
# Create your config file
./configure.sh
smentek
источник
эта последовательность вызовов envsubstдействительно работает.
Alex
Для всех , кто хочет, envsubstне работает на MacOS, вам необходимо установить его с помощью доморощенного: brew install gettext.
Мэтт
9

Более длинная, но более надежная версия принятого ответа:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})?;substr($1,0,int(length($1)/2)).($2&&length($1)%2?$2:$ENV{$3||$4});eg' template.txt

Это расширяет все экземпляры $VAR или ${VAR} до их значений среды (или, если они не определены, до пустой строки).

Он правильно экранирует обратную косую черту и принимает экранированный обратной косой чертой знак $, чтобы запретить подстановку (в отличие от envsubst, которая, как оказалось, этого не делает ).

Итак, если ваша среда:

FOO=bar
BAZ=kenny
TARGET=backslashes
NOPE=engi

и ваш шаблон:

Two ${TARGET} walk into a \\$FOO. \\\\
\\\$FOO says, "Delete C:\\Windows\\System32, it's a virus."
$BAZ replies, "\${NOPE}s."

результат будет:

Two backslashes walk into a \bar. \\
\$FOO says, "Delete C:\Windows\System32, it's a virus."
kenny replies, "${NOPE}s."

Если вы хотите избежать обратной косой черты только перед символом $ (вы можете написать "C: \ Windows \ System32" в шаблоне без изменений), используйте эту слегка измененную версию:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\});substr($1,0,int(length($1)/2)).(length($1)%2?$2:$ENV{$3||$4});eg' template.txt
Стюарт П. Бентли
источник
8

Я бы сделал это таким образом, вероятно, менее эффективным, но более легким для чтения / обслуживания.

TEMPLATE='/path/to/template.file'
OUTPUT='/path/to/output.file'

while read LINE; do
  echo $LINE |
  sed 's/VARONE/NEWVALA/g' |
  sed 's/VARTWO/NEWVALB/g' |
  sed 's/VARTHR/NEWVALC/g' >> $OUTPUT
done < $TEMPLATE
Craig552uk
источник
10
Вы можете сделать это, не читая строку за строкой и используя только один вызов sed:sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
Брэндон Блум,
8

Если вы хотите использовать шаблоны Jinja2 , см. Этот проект: j2cli .

Поддерживает:

  • Шаблоны из файлов JSON, INI, YAML и входных потоков
  • Создание шаблонов из переменных среды
kolypto
источник
5

Принимая ответ от ZyX с использованием чистого bash, но с сопоставлением регулярных выражений нового стиля и косвенной заменой параметров, он становится:

#!/bin/bash
regex='\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}'
while read line; do
    while [[ "$line" =~ $regex ]]; do
        param="${BASH_REMATCH[1]}"
        line=${line//${BASH_REMATCH[0]}/${!param}}
    done
    echo $line
done
которым
источник
5

Если использование Perl - вариант, и вы довольны основанием расширений только на переменных среды (в отличие от всех переменных оболочки ), рассмотрите надежный ответ Стюарта П. Бентли .

Этот ответ направлен на предоставление решения только для bash, которое, несмотря на его использование, evalдолжно быть безопасным в использовании .

Эти цели являются:

  • Поддержка расширения ссылок на обе ${name}и $nameпеременные.
  • Запретить все другие расширения:
    • подстановки команд ( $(...)и унаследованный синтаксис `...`)
    • арифметические замены ( $((...))и унаследованный синтаксис $[...]).
  • Разрешить выборочное подавление раскрытия переменных с помощью префикса \( \${name}).
  • Сохраняйте специальные символы. на входе, в частности, "и \экземпляры.
  • Разрешить ввод через аргументы или через стандартный ввод.

ФункцияexpandVars() :

expandVars() {
  local txtToEval=$* txtToEvalEscaped
  # If no arguments were passed, process stdin input.
  (( $# == 0 )) && IFS= read -r -d '' txtToEval
  # Disable command substitutions and arithmetic expansions to prevent execution
  # of arbitrary commands.
  # Note that selectively allowing $((...)) or $[...] to enable arithmetic
  # expressions is NOT safe, because command substitutions could be embedded in them.
  # If you fully trust or control the input, you can remove the `tr` calls below
  IFS= read -r -d '' txtToEvalEscaped < <(printf %s "$txtToEval" | tr '`([' '\1\2\3')
  # Pass the string to `eval`, escaping embedded double quotes first.
  # `printf %s` ensures that the string is printed without interpretation
  # (after processing by by bash).
  # The `tr` command reconverts the previously escaped chars. back to their
  # literal original.
  eval printf %s "\"${txtToEvalEscaped//\"/\\\"}\"" | tr '\1\2\3' '`(['
}

Примеры:

$ expandVars '\$HOME="$HOME"; `date` and $(ls)'
$HOME="/home/jdoe"; `date` and $(ls)  # only $HOME was expanded

$ printf '\$SHELL=${SHELL}, but "$(( 1 \ 2 ))" will not expand' | expandVars
$SHELL=/bin/bash, but "$(( 1 \ 2 ))" will not expand # only ${SHELL} was expanded
  • По соображениям производительности функция считывает ввод stdin сразу в память, но ее легко адаптировать к построчному подходу.
  • Также поддерживает неосновные расширения переменных, например ${HOME:0:10}, если они не содержат встроенных команд или арифметических подстановок, таких как${HOME:0:$(echo 10)}
    • Такие встроенные замены фактически BREAK функции (потому что все $(и `экземпляры слепо экранирование).
    • Точно так же искаженные ссылки на переменные, такие как ${HOME(отсутствие закрытия }) BREAK функции.
  • Из-за того, что bash обрабатывает строки с двойными кавычками, обратная косая черта обрабатывается следующим образом:
    • \$name предотвращает расширение.
    • Сингл, \за которым не следует, $сохраняется как есть.
    • Если вы хотите представить несколько смежных \ экземпляров, вы должны удвоить их ; например:
      • \\-> \- так же, как просто\
      • \\\\ -> \\
    • Вход не должен содержать следующее (редко используемые символы), которые используются для внутренних целей: 0x1, 0x2, 0x3.
  • Существует в основном гипотетическая проблема: если bash представит новый синтаксис расширения, эта функция не сможет предотвратить такие расширения - см. Ниже решение, которое не использует eval.

Если вы ищете более ограниченное решение, которое поддерживает только${name} расширения, то есть с обязательными фигурными скобками, игнорируя $nameссылки, см. Этот мой ответ .


Вот улучшенная версия evalбесплатного решения только для bash из принятого ответа :

Усовершенствования:

  • Поддержка расширения как ссылок, так ${name}и $nameпеременных.
  • Поддержка \-экранирование ссылок на переменные, которые не следует расширять.
  • В отличие от evalрешения на основе выше,
    • неосновные расширения игнорируются
    • неверно сформированные ссылки на переменные игнорируются (они не нарушают работу скрипта)
 IFS= read -d '' -r lines # read all input from stdin at once
 end_offset=${#lines}
 while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do
      pre=${BASH_REMATCH[1]} # everything before the var. reference
      post=${BASH_REMATCH[5]}${lines:end_offset} # everything after
      # extract the var. name; it's in the 3rd capture group, if the name is enclosed in {...}, and the 4th otherwise
      [[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]}
      # Is the var ref. escaped, i.e., prefixed with an odd number of backslashes?
      if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then
           : # no change to $lines, leave escaped var. ref. untouched
      else # replace the variable reference with the variable's value using indirect expansion
           lines=${pre}${!varName}${post}
      fi
      end_offset=${#pre}
 done
 printf %s "$lines"
mklement0
источник
5

Вот еще одно чистое решение bash:

  • он использует heredoc, поэтому:
    • сложность не увеличивается из-за дополнительного необходимого синтаксиса
    • шаблон может включать код bash
      • это также позволяет вам правильно делать отступы. Увидеть ниже.
  • он не использует eval, поэтому:
    • нет проблем с рендерингом конечных пустых строк
    • нет проблем с котировками в шаблоне

$ cat code

#!/bin/bash
LISTING=$( ls )

cat_template() {
  echo "cat << EOT"
  cat "$1"
  echo EOT
}

cat_template template | LISTING="$LISTING" bash

$ cat template (с завершающими символами новой строки и двойными кавычками)

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
$( echo "$LISTING" | sed 's/^/        /' )
      <pre>
    </p>
  </body>
</html>

вывод

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
        code
        template
      <pre>
    </p>
  </body>
</html>
Томаш Поспишек
источник
4

Вот еще одно решение: сгенерируйте сценарий bash со всеми переменными и содержимым файла шаблона, этот сценарий будет выглядеть так:

word=dog           
i=1                
cat << EOF         
the number is ${i} 
the word is ${word}

EOF                

Если мы загрузим этот скрипт в bash, он выдаст желаемый результат:

the number is 1
the word is dog

Вот как сгенерировать этот сценарий и передать его в bash:

(
    # Variables
    echo word=dog
    echo i=1

    # add the template
    echo "cat << EOF"
    cat template.txt
    echo EOF
) | bash

обсуждение

  • Скобки открывают вспомогательную оболочку, ее цель - сгруппировать вместе весь сгенерированный вывод.
  • Во вспомогательной оболочке мы генерируем все объявления переменных
  • Также во вспомогательной оболочке мы генерируем catкоманду с HEREDOC
  • Наконец, мы передаем вывод вспомогательной оболочки в bash и производим желаемый результат
  • Если вы хотите перенаправить этот вывод в файл, замените последнюю строку на:

    ) | bash > output.txt
Хай вю
источник
3

Эта страница описывает ответ с awk

awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < input.txt > output.txt
Мэтт Браун
источник
Это сохраняет все цитаты нетронутыми. Большой!
Pepster
3

Прекрасный чехол для штпл . (мой проект, поэтому он не так широко используется и не имеет документации. Но вот решение, которое он предлагает. Можете ли вы его протестировать.)

Просто выполните:

$ i=1 word=dog sh -c "$( shtpl template.txt )"

Результат:

the number is 1
the word is dog

Радоваться, веселиться.

zstegi
источник
1
Если это чушь, то все равно проиграют. И меня это устраивает. Но хорошо, замечено, что это не очень хорошо видно, что это на самом деле мой проект. Сделаем это более заметным в будущем. В любом случае спасибо за ваш комментарий и ваше время.
zstegi 03
Я хочу добавить, что вчера я действительно искал варианты использования, где shtpl был бы идеальным решением. Да, мне было скучно ...
zstegi
3
# Usage: template your_file.conf.template > your_file.conf
template() {
        local IFS line
        while IFS=$'\n\r' read -r line ; do
                line=${line//\\/\\\\}         # escape backslashes
                line=${line//\"/\\\"}         # escape "
                line=${line//\`/\\\`}         # escape `
                line=${line//\$/\\\$}         # escape $
                line=${line//\\\${/\${}       # de-escape ${         - allows variable substitution: ${var} ${var:-default_value} etc
                # to allow arithmetic expansion or command substitution uncomment one of following lines:
#               line=${line//\\\$\(/\$\(}     # de-escape $( and $(( - allows $(( 1 + 2 )) or $( command ) - UNSECURE
#               line=${line//\\\$\(\(/\$\(\(} # de-escape $((        - allows $(( 1 + 2 ))
                eval "echo \"${line}\"";
        done < "$1"
}

Это чистая функция bash, настраиваемая по вашему вкусу, используемая в продакшене и не должна прерываться при вводе. Если сломается - дайте знать.

ТТТ
источник
0

Вот функция bash, которая сохраняет пробелы:

# Render a file in bash, i.e. expand environment variables. Preserves whitespace.
function render_file () {
    while IFS='' read line; do
        eval echo \""${line}"\"
    done < "${1}"
}
Игорь Кацон
источник
0

Вот модифицированный perlсценарий, основанный на нескольких других ответах:

perl -pe 's/([^\\]|^)\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}/$1.$ENV{$2}/eg' -i template

Возможности (в зависимости от моих потребностей, но должны быть легко изменены):

  • Пропускает экранированные раскрытия параметров (например, \ $ {VAR}).
  • Поддерживает раскрытие параметров в форме $ {VAR}, но не $ VAR.
  • Заменяет $ {VAR} пустой строкой, если переменная VAR отсутствует.
  • В имени поддерживаются только символы az, AZ, 0-9 и подчеркивания (за исключением цифр в первой позиции).
Kevin
источник
0

Посмотрите на скрипт Python для простой замены переменных здесь: https://github.com/jeckep/vsubst

Использовать очень просто:

python subst.py --props secure.properties --src_path ./templates --dst_path ./dist
jeckep
источник
0

Чтобы продолжить ответ plockc на этой странице, вот версия, подходящая для тире, для тех из вас, кто хочет избежать башизмов.

eval "cat <<EOF >outputfile
$( cat template.in )
EOF
" 2> /dev/null
bgStack15
источник