Я пишу сценарий для автоматизации конфигурационных файлов создания для 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"
BTW, как я перенаправляю вывод во внешний файл здесь? Я должен выйти из чего-то, если переменные содержат, скажем, кавычки?
2) Используя кошку и sed для замены каждой переменной с ее значением:
Учитывая template.txt:
The number is ${i}
The word is ${word}
Команда:
cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${word}/dog/"
Кажется плохим мне из-за потребности выйти из многих различных символов, и со многими переменными строка будет tooooo долго.
Можно ли думать о некотором другом изящном и безопасном решении?
Вы можете использовать это:
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}"
WARNING: Я не знаю способа корректной обработки ввода с NUL в bash или сохранения количества новых строк в конце строки. Последний вариант представлен так, как он есть, потому что оболочки "любят" двоичный ввод:
read
будет интерпретировать обратные слеши. read -r
не будет интерпретировать обратные слеши, но все равно отбросит последнюю строку, если она не заканчивается новой строкой. "$(...)"
удалит столько новых строк, сколько их есть, поэтому я заканчиваю ...
символом ; echo -n a
и использую echo -n "${line:0:-1}"
: это удаляет последний символ (который есть a
) и сохраняет столько новых строк, сколько было во входных данных (включая отсутствие). Я согласен с использованием 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
Я бы сделал это таким способом, вероятно, менее эффективным, но более легким для чтения / поддержки.
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