我正在编写一个脚本来自动为我自己的网络服务器创建 Apache 和 PHP 的配置文件。我不想使用任何 GUI,如 CPanel 或 ISPConfig。
我有一些 Apache 和 PHP 配置文件的模板。 Bash 脚本需要读取模板,进行变量替换并将解析后的模板输出到某个文件夹中。最好的方法是什么?我可以想出几种办法。哪一种是最好的,或者可能有一些更好的方法可以做到这一点?我想在纯 Bash 中执行此操作(例如,在 PHP 中很容易)
- 如何替换文本文件中的 ${} 占位符?
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"
顺便说一句,如何将输出重定向到此处的外部文件?如果变量包含引号,我是否需要转义某些内容?
- 使用 cat & sed 用它的值替换每个变量:
给定 template.txt (见上文)
命令:
cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${word}/dog/"
对我来说似乎很糟糕,因为需要转义许多不同的符号,并且对于许多变量,该行将太长。
您能想到其他优雅且安全的解决方案吗?
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.
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)
- How to replace ${} placeholders in a text file?
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, how do I redirect output to external file here? Do I need to escape something if variables contain, say, quotes?
- Using cat & sed for replacing each variable with its value:
Given template.txt (see above)
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?
发布评论
评论(26)
尝试
envsubst
Try
envsubst
heredoc 是一种构建 conf 文件模板的内置方法。
关于yottsa的回答:
envsubst
对我来说是新的。极好的。A heredoc is a builtin way to template a conf file.
Regarding yottsa's answer:
envsubst
was new to me. Fantastic.您可以使用以下命令:
将所有
${...}
字符串替换为相应的环境变量(不要忘记在运行此脚本之前导出它们)。对于纯 bash,这应该可以工作(假设变量不包含 ${...} 字符串)
:如果 RHS 引用某个引用自身的变量,则解决方案不会挂起:
警告:我不知道如何正确处理 bash 中的 NUL 输入或保留尾随换行符的数量。最后一个变体之所以如此呈现,是因为 shell“喜欢”二进制输入:
read
将解释反斜杠。"$(...)"
将删除尽可能多的尾随换行符,因此我以结束
并使用...
; echo -n aecho -n "${line:0:-1}"
:这会删除最后一个字符(即a
)并保留输入中有多少个尾随换行符(包括没有)。You can use this:
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):
. Solution that does not hang if RHS references some variable that references itself:
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:
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).我同意使用 sed:它是搜索/替换的最佳工具。这是我的方法:
I agree with using sed: it is the best tool for search/replace. Here is my approach:
我有一个像 mogsie 这样的 bash 解决方案,但使用heredoc而不是herestring来避免转义双引号
I have a bash solution like mogsie but with heredoc instead of herestring to allow you to avoid escaping double quotes
尝试 eval
我认为
eval
效果非常好。它处理带有换行符、空格和各种 bash 内容的模板。当然,如果您可以完全控制模板本身:当然,应该谨慎使用此方法,因为 eval 可以执行任意代码。以 root 身份运行它几乎是不可能的。模板中的引号需要转义,否则会被
eval
吃掉。如果您更喜欢
cat
而不是echo
,您也可以使用此处文档@plockc 提供了一个避免 bash 引号转义问题的解决方案:
编辑: 删除了部分关于使用 sudo 以 root 身份运行它...
编辑: 添加了有关如何转义引号的评论,添加了 plockc 的解决方案!
Try eval
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: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
.You can also use here documents if you prefer
cat
toecho
@plockc provoded a solution that avoids the bash quote escaping issue:
Edit: Removed part about running this as root using sudo...
Edit: Added comment about how quotes need to be escaped, added plockc's solution to the mix!
编辑 2017 年 1 月 6 日
我需要在配置文件中保留双引号,因此使用 sed 进行双转义双引号会有所帮助:
我无法想到保留尾随新行,但会保留中间的空行。
虽然这是一个老话题,但我在这里找到了更优雅的解决方案: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/
所有功劳都归功于格雷戈里·帕科斯。
Edit Jan 6, 2017
I needed to keep double quotes in my configuration file so double escaping double quotes with sed helps:
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/
All credits to Grégory Pakosz.
与其重新发明轮子,不如使用 envsubst
几乎可以在任何场景中使用,例如从 Docker 容器中的环境变量构建配置文件。
如果在 Mac 上,请确保您有 homebrew 然后从 gettext 链接它:
./template.cfg
./.env:
./ configure.sh
现在只需使用它:
Instead of reinventing the wheel go with envsubst
Can be used in almost any scenario, for instance building configuration files from environment variables in docker containers.
If on mac make sure you have homebrew then link it from gettext:
./template.cfg
./.env:
./configure.sh
Now just use it:
我会这样做,可能效率较低,但更容易阅读/维护。
I'd have done it this way, probably less efficient, but easier to read/maintain.
如果您想使用 Jinja2 模板,请参阅此项目:j2cli。
它支持:
If you want to use Jinja2 templates, see this project: j2cli.
It supports:
已接受答案的更长但更强大的版本:
这会扩展
$VAR
或${VAR}
的所有实例> 到它们的环境值(或者,如果它们未定义,则为空字符串)。它正确地转义反斜杠,并接受反斜杠转义的 $ 来抑制替换(与 envsubst 不同,事实证明,它不这样做)。
因此,如果您的环境是:
并且您的模板是:
结果将是:
如果您只想在 $ 之前转义反斜杠(您可以在模板中不变地写“C:\Windows\System32”),请使用此稍微修改的版本:
A longer but more robust version of the accepted answer:
This expands all instances of
$VAR
or${VAR}
to their environment values (or, if they're undefined, the empty string).It properly escapes backslashes, and accepts a backslash-escaped $ to inhibit substitution (unlike envsubst, which, it turns out, doesn't do this).
So, if your environment is:
and your template is:
the result would be:
If you only want to escape backslashes before $ (you could write "C:\Windows\System32" in a template unchanged), use this slightly-modified version:
这是另一个解决方案:生成一个包含所有变量和模板文件内容的 bash 脚本,该脚本将如下所示:
如果我们将此脚本输入 bash,它将产生所需的输出:
以下是如何生成该脚本和将该脚本输入 bash:
讨论
cat 命令与 HEREDOC
如果您想将此输出重定向到文件中,请将最后一行替换为:
<前><代码>) |重击>输出.txt
Here is another solution: generate a bash script with all the variables and the contents of the template file, that script would look like this:
If we feed this script into bash it would produce the desired output:
Here is how to generate that script and feed that script into bash:
Discussion
cat
command with HEREDOCIf you want to redirect this output into a file, replace the last line with:
这是另一个纯 bash 解决方案:
$ cat code
输入:
$ cat template
(带有尾随换行符和双引号)输出:
Here's another pure bash solution:
$ cat code
Input:
$ cat template
(with trailing newlines and double quotes)Output:
使用纯 bash 从 ZyX 获取答案,但使用新样式的正则表达式匹配和间接参数替换,它变成:
Taking the answer from ZyX using pure bash but with new style regex matching and indirect parameter substitution it becomes:
如果使用Perl是一种选择,并且您满足于仅基于环境变量进行基础扩展(而不是所有shell 变量),考虑Stuart P. Bentley 的稳健答案。
这个答案旨在提供一个仅 bash 的解决方案,尽管使用了
eval
- 应该可以安全使用。目标是:
${name}
和$name
变量引用的扩展。$(...)
和旧语法`...`
)$((...))
和旧语法$[...]
)。\
(\${name}
) 来选择性地抑制变量扩展。"
和\
实例。函数
expandVars()
示例:
${HOME:0:10}
,只要它们不包含嵌入式命令或算术替换,例如${首页:0:$(回显10)}
$(
和`
实例都会被盲目转义)。${HOME
(缺少结束}
)会破坏函数。\$name
防止扩展。$
的单个\
将按原样保留。\
实例,则必须将它们加倍;例如:\\
->\
- 与\
相同
\\\\
-> <代码>\\0x1
、0x2
、0x3
.eval
的解决方案。如果您正在寻找限制性更强的解决方案,仅支持
${name}
扩展 - 即强制 > 大括号,忽略$name
引用 - 请参阅我的这个答案。这是来自已接受的答案<的仅 bash、
eval
-free 解决方案的改进版本/a>:改进如下:
${name}
和$name
变量引用。\
- 转义不应扩展的变量引用。eval
的解决方案不同,If using Perl is an option and you're content with basing expansions on environment variables only (as opposed to all shell variables), consider Stuart P. Bentley's robust answer.
This answer aims to provide a bash-only solution that - despite use of
eval
- should be safe to use.The goals are:
${name}
and$name
variable references.$(...)
and legacy syntax`...`
)$((...))
and legacy syntax$[...]
).\
(\${name}
)."
and\
instances.Function
expandVars()
:Examples:
${HOME:0:10}
, as long as they contain no embedded command or arithmetic substitutions, such as${HOME:0:$(echo 10)}
$(
and`
instances are blindly escaped).${HOME
(missing closing}
) BREAK the function.\$name
prevents expansion.\
not followed by$
is preserved as is.\
instances, you must double them; e.g.:\\
->\
- the same as just\
\\\\
->\\
0x1
,0x2
,0x3
.eval
.If you're looking for a more restrictive solution that only supports
${name}
expansions - i.e., with mandatory curly braces, ignoring$name
references - see this answer of mine.Here is an improved version of the bash-only,
eval
-free solution from the accepted answer:The improvements are:
${name}
and$name
variable references.\
-escaping variable references that shouldn't be expanded.eval
-based solution above,为了跟进本页上的plockc的答案,这里有一个适合破折号的版本,适合那些希望避免bashisms的人。
To follow up on plockc's answer on this page, here is a dash-suitable version, for those of you looking to avoid bashisms.
尝试 shtpl
shtpl 的完美案例。 (我的项目,所以它没有被广泛使用并且缺乏文档。但是这里是它提供的解决方案。也许你想测试它。)
只需执行:
结果是:
玩得开心。
Try shtpl
Perfect case for shtpl. (project of mine, so it is not widely in use and lacks in documentation. But here is the solution it offers anyhow. May you want to test it.)
Just execute:
Result is:
Have fun.
本页描述了使用 awk 的答案
This page describes an answer with awk
这是纯 bash 函数,可根据您的喜好进行调整,在生产中使用,并且不应因任何输入而中断。
如果它坏了 - 请告诉我。
This is the pure bash function adjustable to your liking, used in production and should not break on any input.
If it breaks - let me know.
您还可以使用bashible(它内部使用上面/下面描述的评估方法)。
有一个示例,如何从多个部分生成 HTML:
https://github .com/mig1984/bashible/tree/master/examples/templates
You can also use bashible (which internally uses the evaluating approach described above/below).
There is an example, how to generate a HTML from multiple parts:
https://github.com/mig1984/bashible/tree/master/examples/templates
在这里查看简单的变量替换 python 脚本: https://github.com/jeckep/vsubst
它是使用非常简单:
Look at simple variables substitution python script here: https://github.com/jeckep/vsubst
It is very simple to use:
您可以使用带有环境变量的模板文件并运行
You can use a template file with environment variables and run
这是一个保留空格的 bash 函数:
Here's a bash function that preserves whitespace:
这是一个基于其他一些答案的修改后的
perl
脚本:功能(基于我的需要,但应该很容易修改):
Here's a modified
perl
script based on a few of the other answers:Features (based on my needs, but should be easy to modify):
您还可以使用 printf 来填充模板。
这是输出,引号和空格完好无损:
You can also use printf to fill a template.
Here's the output, with quotes and whitespace intact:
我一直在寻找一个仅标准实用程序的解决方案,而且我对评估任意模板感到不舒服——这很容易出错。最终我想出了以下方法。
模板文件:
Bash 脚本:
以及运行它的结果:
它相当粗糙,但似乎在典型的 Linux 和 BSD 上都按预期工作......抱歉,我的意思是 macOS ;-)
I was looking for a standard-utils-only solution and also I was not comfortable with evaling an arbitrary template – this can go wrong only too easily. Eventually I came up with the following approach.
Template file:
Bash script:
And the result of running it:
It's rather crude though seems to be working as intended both on typical Linux and BSD… sorry, I mean macOS ;-)