使用 bash 脚本从模板创建新文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6214743/
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
Create new file from templates with bash script
提问by kheraud
I have to create conf files and init.d
which are very similar. These files permit to deploy new http service on my servers. These files are the same and only some parameters change from one file to another (listen_port
, domain, path on server...).
我必须创建init.d
非常相似的conf 文件。这些文件允许在我的服务器上部署新的 http 服务。这些文件是相同的,只有一些参数从一个文件更改为另一个文件(listen_port
、域、服务器上的路径...)。
As any error in these files leads to misfunction of service I would like to create these files using a bash script.
由于这些文件中的任何错误都会导致服务功能失常,我想使用 bash 脚本创建这些文件。
For example:
例如:
generate_new_http_service.sh 8282 subdomain.domain.com /home/myapp/rootOfHTTPService
I am looking for a kind of templating module that I could use with bash. This templating module would use some generic conf and init.d scripts to create new ones.
我正在寻找一种可以与 bash 一起使用的模板模块。这个模板模块会使用一些通用的 conf 和 init.d 脚本来创建新的。
Do you have hints for that? If not I could use python templating engine.
你有什么提示吗?如果没有,我可以使用 python 模板引擎。
回答by dogbane
You can do this using a heredoc. e.g.
您可以使用 heredoc 来做到这一点。例如
generate.sh:
生成.sh:
#!/bin/sh
#define parameters which are passed in.
PORT=
DOMAIN=
#define the template.
cat << EOF
This is my template.
Port is $PORT
Domain is $DOMAIN
EOF
Output:
输出:
$ generate.sh 8080 domain.com
This is my template.
Port is 8080
Domain is domain.com
or save it to a file:
或将其保存到文件:
$ generate.sh 8080 domain.com > result
回答by kheraud
Template module for bash? Use sed
, Luke! Here is an example of one of millions of possible ways of doing this:
bash的模板模块?使用sed
,卢克!这是执行此操作的数百万种可能方法之一的示例:
$ cat template.txt
#!/bin/sh
echo Hello, I am a server running from %DIR% and listening for connection at %HOST% on port %PORT% and my configuration file is %DIR%/server.conf
$ cat create.sh
#!/bin/sh
sed -e "s;%PORT%;;g" -e "s;%HOST%;;g" -e "s;%DIR%;;g" template.txt > script.sh
$ bash ./create.sh 1986 example.com /tmp
$ bash ./script.sh
Hello, I am a server running from /tmp and listening for connection at example.com on port 1986 and my configuration file is /tmp/server.conf
$
回答by Kim Stebel
you can do this directly in bash, you do not even need sed. Write a script like that:
您可以直接在 bash 中执行此操作,甚至不需要 sed。写一个这样的脚本:
#!/bin/bash
cat <<END
this is a template
with $foo
and $bar
END
then call it like so:
然后像这样调用它:
foo=FOO bar=BAR ./template
回答by FooF
For simple file generation, basically doing
对于简单的文件生成,基本上做
. "${config_file}"
template_str=$(cat "${template_file}")
eval "echo \"${template_str}\""
would suffice.
就足够了。
Here ${config_file}
contains the configuration variables in shell parseable format, and ${template_file}
is the template file that looks like shell here document. The first line sources in the file ${config_file}
, the second line puts the contents of the file ${template_file}
into the shell variable template_str
. Finally in the third line we build the shell command echo "${template_str}"
(where the double quoted expression "${template_str}"
is expanded) and evaluate it.
这里${config_file}
包含shell可解析格式的配置变量,${template_file}
是一个看起来像shell here文档的模板文件。第一行是文件中的源代码${config_file}
,第二行是将文件内容${template_file}
放入 shell 变量中template_str
。最后在第三行中,我们构建了 shell 命令echo "${template_str}"
(其中展开了双引号表达式"${template_str}"
)并对其求值。
For an example of the contents of those two files, please refer to https://serverfault.com/a/699377/120756.
有关这两个文件的内容示例,请参阅https://serverfault.com/a/699377/120756。
There are limitations what you can have in the template file or you need to perform shell escaping. Also if the template file is externally produced, then for security reasons you need to consider implementing a proper filtering prior to execution so that you will not for example lose your files when somebody injects the famous $(rm -rf /)
in the template file.
您可以在模板文件中拥有哪些限制,或者您需要执行 shell 转义。此外,如果模板文件是外部生成的,那么出于安全原因,您需要考虑在执行之前实施适当的过滤,这样您就不会在有人$(rm -rf /)
在模板文件中注入著名文件时丢失您的文件。
回答by Samuel Phan
[Edit] I changed my answer from the original one, that was years ago.
[编辑] 我从几年前的原始答案中更改了我的答案。
I like the answer from FooF above: https://stackoverflow.com/a/30872526/3538173
我喜欢上面 FooF 的答案:https://stackoverflow.com/a/30872526/3538173
Yet, I prefer not to have an intermediary variable to store the whole content of the template file in memory.
然而,我不希望有一个中间变量来将模板文件的全部内容存储在内存中。
. "${config_file}"
eval "echo \"$(cat "${template_file}")\""
Example
例子
Create a template file. Let's call it example.tpl
:
创建模板文件。让我们称之为example.tpl
:
Hello, ${NAME}!
Today, the weather is ${WEATHER}. Enjoy!
Create a configuration file to store your variables. Let's call it good.conf
:
创建一个配置文件来存储您的变量。让我们称之为good.conf
:
NAME=John
WEATHER=good
Now, in the script where you want to render the template, you can write this:
现在,在要渲染模板的脚本中,您可以这样写:
#!/usr/bin/env bash
template_file=example.tpl
config_file=good.conf
. "${config_file}"
eval "echo \"$(cat "${template_file}")\""
# Or store the output in a file
eval "echo \"$(cat "${template_file}")\"" > out
You should see this wonderful output :)
你应该看到这个美妙的输出:)
Hello, John!
Today, the weather is good. Enjoy!
Caution with eval
小心与 eval
When you use eval
, if the template file contains some instructions, they will be executed, and it can be dangerous. For example, let's change the example.tpl
above with this content:
当您使用 时eval
,如果模板文件包含一些指令,它们将被执行,并且可能是危险的。例如,让我们example.tpl
用以下内容更改上面的内容:
Hello, ${NAME}!
Today, the weather is ${WEATHER}. Enjoy!
I'm a hacker, hu hu! Look, fool!
$(ls /)
Now, if you render your template file, you will see this:
现在,如果您渲染模板文件,您将看到:
Hello, John!
Today, the weather is good. Enjoy!
I'm a hacker, hu hu! Look, fool!
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
Now edit your file good.conf
to have this content:
现在编辑您的文件good.conf
以包含以下内容:
NAME=$(ls -l /var)
WEATHER=good
and render the template. You should see something like this:
并渲染模板。您应该会看到如下内容:
Hello, total 8
drwxr-xr-x. 2 root root 6 Apr 11 04:59 adm
drwxr-xr-x. 5 root root 44 Sep 11 18:04 cache
drwxr-xr-x. 3 root root 34 Sep 11 18:04 db
drwxr-xr-x. 3 root root 18 Sep 11 18:04 empty
drwxr-xr-x. 2 root root 6 Apr 11 04:59 games
drwxr-xr-x. 2 root root 6 Apr 11 04:59 gopher
drwxr-xr-x. 3 root root 18 May 9 13:48 kerberos
drwxr-xr-x. 28 root root 4096 Oct 8 00:30 lib
drwxr-xr-x. 2 root root 6 Apr 11 04:59 local
lrwxrwxrwx. 1 root root 11 Sep 11 18:03 lock -> ../run/lock
drwxr-xr-x. 8 root root 4096 Oct 8 04:55 log
lrwxrwxrwx. 1 root root 10 Sep 11 18:03 mail -> spool/mail
drwxr-xr-x. 2 root root 6 Apr 11 04:59 nis
drwxr-xr-x. 2 root root 6 Apr 11 04:59 opt
drwxr-xr-x. 2 root root 6 Apr 11 04:59 preserve
lrwxrwxrwx. 1 root root 6 Sep 11 18:03 run -> ../run
drwxr-xr-x. 8 root root 87 Sep 11 18:04 spool
drwxrwxrwt. 4 root root 111 Oct 9 09:02 tmp
drwxr-xr-x. 2 root root 6 Apr 11 04:59 yp!
Today, the weather is good. Enjoy!
I'm a hacker, hu hu! Look, fool!
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
swapfile
sys
tmp
usr
var
As you can see, command injection in the configuration fileand the template fileis possible, and that's why you have to be extra careful:
如您所见,配置文件和模板文件中的命令注入是可能的,这就是您必须格外小心的原因:
- be sure of the content of the template file: check that there is NO command injection.
- be sure of the content of the configuration file: check that there is NO command injection as well. If the configuration file comes from someone else, you need to know and trust that person before rendering the template.
- 确保模板文件的内容:检查是否没有命令注入。
- 确保配置文件的内容:检查是否也没有命令注入。如果配置文件来自其他人,您需要在渲染模板之前了解并信任该人。
Imagine that you are a password-less sudoer, rendering the template file could result in ruining your system with a well-placed rm -rf
.
想象一下,你是一个没有密码的 sudoer,渲染模板文件可能会导致你的系统因放置良好的rm -rf
.
As long as you control the content of these files, it is fine to use this eval
templating.
只要您控制这些文件的内容,就可以使用此eval
模板。
If you have an external (untrusted) incoming configuration file, you should look for templating engine, that will isolate these kind of injection. For example, Jinja2 templatingis quite famous in Python.
如果您有一个外部(不受信任的)传入配置文件,您应该寻找模板引擎,它将隔离这些类型的注入。例如,Jinja2 模板在 Python 中非常有名。
回答by Keegs
Here's the approach that I ended up taking to solve this problem. I found it a little more flexible than some of the above approaches, and it avoids some of the issues with quotes.
这是我最终用来解决这个问题的方法。我发现它比上述一些方法更灵活,并且它避免了一些引号问题。
fill.sh:
填充.sh:
#!/usr/bin/env sh
config=""
template=""
destination=""
cp "$template" "$destination"
while read line; do
setting="$( echo "$line" | cut -d '=' -f 1 )"
value="$( echo "$line" | cut -d '=' -f 2- )"
sed -i -e "s;%${setting}%;${value};g" "$destination"
done < "$config"
template:
模板:
Template full of important %THINGS%
"Note that quoted %FIELDS% are handled correctly"
If I need %NEWLINES% then I can add them as well.
config:
配置:
THINGS=stuff
FIELDS="values work too!"
NEWLINES="those\nnifty\nlinebreaks"
result:Template full of important stuff
结果:充满重要内容的模板
"Note that quoted "values work too!" are handled correctly"
If I need those
nifty
linebreaks then I can add them as well.
回答by Alexey
You can use python class string.Template
您可以使用 python 类 string.Template
$ echo 'before $X after' > template.txt
$ python -c 'import string; print(string.Template(open("template.txt").read()).substitute({"X":"A"}))'
before A after
or
或者
$ python -c 'import string, sys; print(string.Template(open("template.txt").read()).substitute({"X":sys.argv[1]}))' "A"
Here $X
is a placeholder in the template and {"X":"A"}
is a mapping of the placeholder to a value. In the python code we read the template text from the file, create a template from it, then substitute the placeholder with the command line argument.
这$X
是模板{"X":"A"}
中的占位符,是占位符到值的映射。在 python 代码中,我们从文件中读取模板文本,从中创建一个模板,然后用命令行参数替换占位符。
Alternatively you can use Ruby's ERB, if Ruby is installed on your machine.
或者,如果您的机器上安装了 Ruby,您可以使用 Ruby 的 ERB。
$ echo "before <%= ENV['X'] %> after" > template.txt
$ X=A erb template.txt
before A after
Here <%= ENV['X'] %>
is a placeholder. ENV['X']
reads the value from the environment variable. X=A
sets the environment variable to the desired value.
这<%= ENV['X'] %>
是一个占位符。ENV['X']
从环境变量中读取值。X=A
将环境变量设置为所需的值。
回答by kyb
Elegant and short solution in one line with perl
优雅而简短的解决方案合二为一 perl
I use perl
to replace variables with their values:
我用perl
它们的值替换变量:
export world=World beautiful=wonderful
echo 'I love you, $world! You are $beautiful.' >my_template.txt
perl -pe 's|$([A-Za-z_]+)|$ENV{}|g' my_template.txt
The output: I love you, World! You are wonderful
.
输出:I love you, World! You are wonderful
。
my_template.txt
can contain variables prefixed with $
.
my_template.txt
可以包含前缀为$
.