Bash 模板:如何使用 Bash 从模板构建配置文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2914220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash Templating: How to build configuration files from templates with Bash?
提问by Vladislav Rastrusny
I'm writing a script to automate creating configuration files for Apache and PHP for my own webserver. I don't want to use any GUIs like CPanel or ISPConfig.
我正在编写一个脚本来为我自己的网络服务器自动创建 Apache 和 PHP 的配置文件。我不想使用任何像 CPanel 或 ISPConfig 这样的 GUI。
I have some templates of Apache and PHP configuration files. Bash script needs to read templates, make variable substitution and output parsed templates into some folder. What is the best way to do that? I can think of several ways. Which one is the best or may be there are some better ways to do that? I want to do that in pure Bash (it's easy in PHP for example)
我有一些 Apache 和 PHP 配置文件的模板。Bash 脚本需要读取模板,进行变量替换并将解析后的模板输出到某个文件夹中。最好的方法是什么?我可以想到几种方法。哪一个是最好的,或者可能有一些更好的方法来做到这一点?我想在纯 Bash 中做到这一点(例如在 PHP 中很容易)
1) How to replace ${} placeholders in a text file?
template.txt:
模板.txt:
the number is ${i}
the word is ${word}
script.sh:
脚本.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, how do I redirect output to external file here? Do I need to escape something if variables contain, say, quotes?
顺便说一句,我如何将输出重定向到外部文件?如果变量包含引号,我需要转义吗?
2) Using cat & sed for replacing each variable with its value:
2) 使用 cat & sed 将每个变量替换为其值:
Given template.txt:
给定模板.txt:
The number is ${i}
The word is ${word}
Command:
命令:
cat template.txt | sed -e "s/${i}/1/" | sed -e "s/${word}/dog/"
Seems bad to me because of the need to escape many different symbols and with many variables the line will be tooooo long.
对我来说似乎很糟糕,因为需要转义许多不同的符号,并且有许多变量,该行会太长。
Can you think of some other elegant and safe solution?
你能想到其他一些优雅和安全的解决方案吗?
采纳答案by ZyX
You can use this:
你可以使用这个:
perl -p -i -e 's/$\{([^}]+)\}/defined $ENV{} ? $ENV{} : $&/eg' < template.txt
to replace all ${...}
strings with corresponding enviroment variables (do not forget to export them before running this script).
${...}
用相应的环境变量替换所有字符串(在运行此脚本之前不要忘记导出它们)。
For pure bash this should work (assuming that variables do not contain ${...} strings):
对于纯 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
. Solution that does not hang if RHS references some variable that references itself:
. 如果 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: I do not know a way to correctly handle input with NULs in bash or preserve the amount of trailing newlines. Last variant is presented as it is because shells “love” binary input:
警告:我不知道在 bash 中使用 NUL 正确处理输入或保留尾随换行符数量的方法。最后一个变体按原样呈现是因为 shell “喜欢”二进制输入:
read
will interpret backslashes.read -r
will not interpret backslashes, but still will drop the last line if it does not end with a newline."$(…)"
will strip as many trailing newlines as there are present, so I end…
with; echo -n a
and useecho -n "${line:0:-1}"
: this drops the last character (which isa
) and preserves as many trailing newlines as there was in the input (including no).
read
将解释反斜杠。read -r
不会解释反斜杠,但如果最后一行不以换行符结尾,仍然会删除最后一行。"$(…)"
因为有本将去除尽可能多的尾随换行符,所以我最终…
与; echo -n a
和使用echo -n "${line:0:-1}"
:此下降的最后一个字符(这是a
)和蜜饯尽可能多的尾随换行符有一个在输入(包括无)。
回答by yottatsa
回答by Dan Garthwaite
envsubst was new to me. Fantastic.
envsubst 对我来说是新的。极好的。
For the record, using a heredoc is a great way to template a conf file.
作为记录,使用 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
回答by Hai Vu
I agree with using sed: it is the best tool for search/replace. Here is my approach:
我同意使用 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
回答by mogsie
I think eval works really well. It handles templates with linebreaks, whitespace, and all sorts of bash stuff. If you have full control over the templates themselves of course:
我认为 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"
This method should be used with care, of course, since eval can execute arbitrary code. Running this as root is pretty much out of the question. Quotes in the template need to be escaped, otherwise they will be eaten by eval
.
当然,这个方法应该小心使用,因为 eval 可以执行任意代码。以 root 身份运行它几乎是不可能的。模板中的引号需要转义,否则会被eval
.
You can also use here documents if you prefer cat
to echo
如果您愿意cat
,也可以使用此处的文档echo
$ eval "cat <<< \"$(<template.txt)\"" 2> /dev/null
@plockc provoded a solution that avoids the bash quote escaping issue:
@plockc 提出了一个解决方案,可以避免 bash 引用转义问题:
$ eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Edit:Removed part about running this as root using sudo...
编辑:删除了关于使用 sudo 以 root 身份运行的部分...
Edit:Added comment about how quotes need to be escaped, added plockc's solution to the mix!
编辑:添加了有关如何转义引号的评论,将 plockc 的解决方案添加到混合中!
回答by plockc
I have a bash solution like mogsie but with heredoc instead of herestring to allow you to avoid escaping double quotes
我有一个像 mogsie 这样的 bash 解决方案,但使用 heredoc 而不是 herestring 来避免转义双引号
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
回答by CKK
Edit Jan 6, 2017
编辑 2017 年 1 月 6 日
I needed to keep double quotes in my configuration file so double escaping double quotes with sed helps:
我需要在我的配置文件中保留双引号,因此使用 sed 对双引号进行双转义会有所帮助:
render_template() {
eval "echo \"$(sed 's/\"/\\"/g' )\""
}
I can't think of keeping trailing new lines, but empty lines in between are kept.
我想不出保持尾随新行,但保留了中间的空行。
Although it is an old topic, IMO I found out more elegant solution here: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/
虽然这是一个老话题,但 IMO 我在这里找到了更优雅的解决方案: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 )\""
}
user="Gregory"
render_template /path/to/template.txt > path/to/configuration_file
All credits to Grégory Pakosz.
所有学分都归功于Grégory Pakosz。
回答by smentek
Instead of reinventing the wheel go with envsubstCan be used in almost any scenario, for instance building configuration files from environment variables in docker containers.
使用envsubst代替重新发明轮子 几乎可以在任何场景中使用,例如从 docker 容器中的环境变量构建配置文件。
If on mac make sure you have homebrewthen link it from gettext:
如果在 mac 上确保你有自制软件,然后从 gettext 链接它:
brew install gettext
brew link --force gettext
./template.cfg
./template.cfg
# We put env variables into placeholders here
this_variable_1 = ${SOME_VARIABLE_1}
this_variable_2 = ${SOME_VARIABLE_2}
./.env:
./.env:
SOME_VARIABLE_1=value_1
SOME_VARIABLE_2=value_2
./configure.sh
./configure.sh
#!/bin/bash
cat template.cfg | envsubst > whatever.cfg
Now just use it:
现在只需使用它:
# 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
回答by Stuart P. Bentley
A longer but more robust version of the accepted answer:
接受答案的更长但更强大的版本:
perl -pe 's;(\*)($([a-zA-Z_][a-zA-Z_0-9]*)|$\{([a-zA-Z_][a-zA-Z_0-9]*)\})?;substr(,0,int(length()/2)).(&&length()%2?:$ENV{||});eg' template.txt
This expands all instances of $VAR
or${VAR}
to their environment values (or, if they're undefined, the empty string).
这会将$VAR
或 的所有实例扩展${VAR}
为其环境值(或者,如果未定义,则为空字符串)。
It properly escapes backslashes, and accepts a backslash-escaped $ to inhibit substitution (unlike envsubst, which, it turns out, doesn't do this).
它正确地转义反斜杠,并接受反斜杠转义的 $ 以禁止替换(与 envsubst 不同,事实证明它不这样做)。
So, if your environment is:
因此,如果您的环境是:
FOO=bar
BAZ=kenny
TARGET=backslashes
NOPE=engi
and your template is:
你的模板是:
Two ${TARGET} walk into a \$FOO. \\
\$FOO says, "Delete C:\Windows\System32, it's a virus."
$BAZ replies, "${NOPE}s."
the result would be:
结果将是:
Two backslashes walk into a \bar. \
$FOO says, "Delete C:\Windows\System32, it's a virus."
kenny replies, "${NOPE}s."
If you only want to escape backslashes before $ (you could write "C:\Windows\System32" in a template unchanged), use this slightly-modified version:
如果您只想在 $ 之前转义反斜杠(您可以在未更改的模板中写入 "C:\Windows\System32"),请使用此稍微修改的版本:
perl -pe 's;(\*)($([a-zA-Z_][a-zA-Z_0-9]*)|$\{([a-zA-Z_][a-zA-Z_0-9]*)\});substr(,0,int(length()/2)).(length()%2?:$ENV{||});eg' template.txt