在Shell脚本中使用Linux进行模板制作?


26

我要完成的是:

1.)有一个配置文件作为模板,带有$ version $ path之类的变量(例如apache config)

2)具有一个shell脚本,可以“填充”模板变量并将生成的文件写入磁盘。

使用shell脚本可以做到这一点吗?如果您可以命名一些命令/工具,我可以完成此操作或提供一些良好的链接,我将非常感激。

Answers:


23

这是很有可能的。一种简单的实现方法是将模板文件实际用作脚本并使用诸如

#! /bin/bash
version="1.2.3"
path="/foo/bar/baz"
cat > /tmp/destfile <<-EOF
here is some config for version $version which should
also reference this path $path
EOF

您甚至可以通过指定version=$1and 来使其在命令行上可配置path=$2,因此可以像那样运行它bash script /foo/bar/baz 1.2.3。该-EOF造成的空白行被忽略之前之前,使用普通的<<EOF,如果你不希望这样的行为。

另一种方法是使用sed的搜索和替换功能

#! /bin/bash
version="1.2.3"
path="/foo/bar/baz"
sed -e "s/VERSION/$version/g" -e "s/PATH/$path/" /path/to/templatefile > /tmp/destfile

它将替换字符串VERSION和PATH的每个实例。如果还有其他原因,这些字符串将存在于模板文件中,则您可以搜索并替换为VERSION或%VERSION%或不太可能被意外触发的字符串。


18

除了,没有其他必要的工具/bin/sh。给定形式的模板文件

Version: ${version}
Path: ${path}

甚至包括混合的shell代码

Version: ${version}
Path: ${path}
Cost: ${cost}\$
$(i=1; for w in one two three four; do echo Param${i}: ${w}; i=$(expr $i + 1); done)

和一个shell可解析的配置文件,例如

version="1.2.3-r42"
path="/some/place/under/the/rainbow/where/files/dance/in/happiness"
cost="42"

将其扩展到

Version: 1.2.3-r42
Path: /some/place/under/the/rainbow/where/files/dance/in/happiness
Cost: 42$
Param1: one
Param2: two
Param3: three
Param4: four

实际上,在shell变量中给出了配置文件config_file的路径,在中给出了模板文件的路径template_file,您所需要做的就是:

. ${config_file}
template="$(cat ${template_file})"
eval "echo \"${template}\""

这可能比将完整的Shell脚本作为模板文件(@mtinberg的解决方案)漂亮。

完整的天真模板扩展程序:

#!/bin/sh

PROG=$(basename $0)

usage()
{
    echo "${PROG} <template-file> [ <config-file> ]"
}

expand()
{
    local template="$(cat $1)"
    eval "echo \"${template}\""
}

case $# in
    1) expand "$1";;
    2) . "$2"; expand "$1";;
    *) usage; exit 0;;
esac

这会将扩展输出为标准输出;只需将标准输出重定向到文件或以明显的方式修改以上内容即可生成所需的输出文件。

注意事项:如果文件包含未转义的双引号("),则模板文件扩展将无法工作。出于安全原因,如果模板文件是由外部实体生成的,我们可能应该进行一些明显的健全性检查,或者甚至更好地执行外壳转义转换。


1
仍然很高兴看到人们积极地使用shell脚本来做这样的有趣的事情。我的一个伙伴用shell脚本编写了一个时钟。 github.com/tablespoon/fun/blob/master/cli-clock 确实有趣。
小鸡

2
当我发现的时候看这自己做的这个答案,我实现了非常幼稚的做法,并张贴在Github上:github.com/nealian/bash-unsafe-templates
Pyrocater

9

在Linux CLI中,最简单的方法就是使用envsubst和环境变量。

示例模板文件apache.tmpl

<VirtualHost *:${PORT}>
    ServerName ${SERVER_NAME}
    ServerAlias ${SERVER_ALIAS}
    DocumentRoot "${DOCUMENT_ROOT}"
</VirtualHost>

运行envsubst并将结果输出到新文件my_apache_site.conf

export PORT="443"
export SERVER_NAME="example.com"
export SERVER_ALIAS="www.example.com"
export DOCUMENT_ROOT="/var/www/html/"
envsubst < apache.tmpl > my_apache_site.conf

输出:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot "/var/www/html/"
</VirtualHost>

2
它是GNU gettext的一部分,因此可在所有主要的Unix平台(包括macOS X)以及Microsoft Windows上使用。
tricasse

4

您可能应该研究PuppetChef之类的配置管理系统。这些可以轻松完成您上面描述的操作,甚至更多。


1
谢谢。绝对,我已经安装并运行了Chef。但是,当您必须编写自己的食谱时,这会增加很多开销。我不知道Ruby编程语言,我的结论是。对于更简单的情况(如果可能),使用shell脚本更容易做到这一点。
马库斯

看起来PuppetChef都使用ERB作为模板,并且这很容易上手。给定一个变量name<%= name %>模板中的字符串将被替换为name的值。name显然,两个系统之间在模板外部进行定义的方式有所不同。
Mike Renfro

1
是的(与Chef一起进行模板制作)本身非常简单。但是,使用Chef作为Framework(并编写食谱)需要大量时间。为了使数据进入模板,您需要了解Chef在何处以及如何管理数据源和许多其他内容的“合并”。我已经开始写自己的食谱了,但是在我的特殊情况下,shell脚本的速度要快100倍……
Markus

为Chef或Puppet建立基础架构可能会很痛苦,或者您可以尝试弄清楚如何无头运行它们,这是一个有趣的冒险。Ansible在开箱即用的模式下可以很好地工作,因此可以更好地平衡自己编写脚本。 docs.ansible.com/template_module.html
小鸡

4

如果您想要轻量级且真实的模板,而不是生成新文件的shell代码,通常的选择是sedawkperl。这是一个链接:http : //savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/

我,我会在该类中使用像perl,tcl,python,ruby之类的真实语言。为脚本编写的东西。他们都有很好的,简单的模板工具以及Google中的大量示例。


4

为此使用shtpl。(我的私人项目,这意味着它并未得到广泛使用。但是也许您还是想对其进行测试)

例如,您要从一个csv文件生成一个/ etc / network / interfaces,您可以这样做:

CSV文件内容(此处为test.csv):

eth0;10.1.0.10;255.255.0.0;10.1.0.1
eth1;192.168.0.10; 255.255.255.0;192.168.0.1

模板(此处为interfaces.tpl):

#% IFS=';'
#% while read "Val1" "Val2" "Val3" "Val4"; do
auto $Val1 
iface $Val1 inet static
  address $Val2 
  netmask $Val3 
  gateway $Val4 

#% done < "$CSVFILE"

命令:

$ CSVFILE=test.csv sh -c "$( shtpl interfaces.tpl )"

结果:

auto eth0 
iface eth0 inet static
  address 10.1.0.10 
  netmask 255.255.0.0 
  gateway 10.1.0.1 

auto eth1 
iface eth1 inet static
  address 192.168.0.10 
  netmask  255.255.255.0 
  gateway 192.168.0.1

请享用!


1

我改进了FooF的答案,以便用户不需要手动取消对双引号的转义:

#!/bin/bash
template="$(cat $1)"
template=$(sed 's/\([^\\]\)"/\1\\"/g; s/^"/\\"/g' <<< "$template")
eval "echo \"${template}\""

1

我最近发布了一个bash脚本,该脚本使用类似于jinja的模板语法来完成该任务。它称为cookie。这是一个演示:

Cookie演示


这如何回答这个问题?
RalfFriedl

1
@RalfFriedl我不确定您的意思。我只是重新阅读了一个问题,以为我一定错过了某些东西,但是我没有看到它。事实上,这如何回答任何这是问的问题的一部分?这几乎就像我建立cookie的唯一目的是回答这个问题……虽然我当时在脑海中的措词可能有所不同,但我确实做到了。:)
Bryan Bugyi

我是调解人,来晚了一点,但也许Ralf只是在慈善地提示您解释 Cookie是如何回答问题的,而不是试图慈善地暗示您对自己的项目不太了解,无法判断它是否可以解决某人的问题。
abathur

0

我可能参加这个聚会很晚。但是,我偶然发现了同样的问题,我选择了用几行代码创建自己的BASH模板引擎:

可以说你有这个file.template

# My template
## Author
 - @NAME@ <@EMAIL@>

和这个rules文件:

NAME=LEOPOLDO WINSTON
EMAIL=leothewinston\@leoserver.com

您执行以下命令:

templater rules < file.template

你得到这个:

# My template
## Author
 - LEOPOLDO WINSTON <leothewinston@leoserver.com>

您可以通过以下方式安装它:

 bpkg install vicentebolea/bash-templater

这是项目现场


0

要扩展@FooF的好答案(换行符未在注释中格式化),请使用heredoc +控制字符,您可以允许任意字符文件名:

template() {
    # if [ "$#" -eq 0 ] ; then return; fi # or just error
    eval "cat <<$(printf '\x04\x04\x04');
$(cat $1)
"
}

这接受任何非空字符,并且仅^D在其自己的行上遇到3 个字节时才截断(实际上从不)。zsh甚至支持空终止符,因此printf '\x00\x00\x00'可以正常工作。 template甚至适用于奸诈的文件名,例如:

for num in `seq 10`; do
    template 'foo "$ .html' # works
done

注意外壳模板可以“扩展”任意命令,例如$(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10))。强大的力量

编辑:如果您真的不希望文件对您发动攻击,请sudo -u nobody sh事先(或其他安全用户)使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.