Templating with Linux in a Shell Script?
Solution 1:
This is very possible. A very simple way to implement this would be for the template file to actually be the script and use shell variables such as
#! /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
You could even make this configurable on the command line by specifying version=$1
and path=$2
, so you can run it like bash script /foo/bar/baz 1.2.3
. The -
before EOF causes whitespace before the lines be ignored, use plain <<EOF
if you do not want that behavior.
Another way to do this would be to use the search and replace functionality of 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
which would replace each instance of the strings VERSION and PATH. If there are other reasons those strings would be in the template file you might make your search and replace be VERSION or %VERSION% or something less likely to be triggered accidentally.
Solution 2:
The easiest way to do this simply in Linux CLI is to use envsubst
and Environment Variables.
Example template file apache.tmpl
:
<VirtualHost *:${PORT}>
ServerName ${SERVER_NAME}
ServerAlias ${SERVER_ALIAS}
DocumentRoot "${DOCUMENT_ROOT}"
</VirtualHost>
Run envsubst
and output result to new file 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
Output:
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot "/var/www/html/"
</VirtualHost>
Solution 3:
No tools necessary other than /bin/sh
. Given a template file of the form
Version: ${version}
Path: ${path}
or even with mixed shell code included
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)
and a shell parsable configuration file like
version="1.2.3-r42"
path="/some/place/under/the/rainbow/where/files/dance/in/happiness"
cost="42"
it is a simple matter to expand this to
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
Indeed, given the path to the configuration file in shell variable config_file
and the path to the template file in template_file
, all you need to do is:
. ${config_file}
template="$(cat ${template_file})"
eval "echo \"${template}\""
This is perhaps prettier than having complete shell script as the template file (@mtinberg's solution).
The complete naive template expander program:
#!/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
This will output the expansion to standard output; just redirect standard output to a file or modify the above in obvious fashion to produce the desired output file.
Caveats: Template file expansion would not work if the file contained unescaped double quotes ("
). For security reasons, we should probably include some obvious sanity checks or, even better, perform shell escaping transformation if the template file is generated by external entity.
Solution 4:
If you want lightweight and real templates rather than shell code that generates new files, the usual choices are sed
& awk
or perl
. Here is one link: http://savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/
Me, I'd use a real language like perl, tcl, python, ruby or something else in that class. Something built for scripting. They all have good, simple templating tools and tons of examples in google.
Solution 5:
I use shtpl for that. (private project of mine, which means, it is not widely in use. But maybe you want to test it anyway)
For example you want to generate an /etc/network/interfaces out of a csv-file, you can do it like that:
CSV-file content (here 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
Template (here interfaces.tpl):
#% IFS=';'
#% while read "Val1" "Val2" "Val3" "Val4"; do
auto $Val1
iface $Val1 inet static
address $Val2
netmask $Val3
gateway $Val4
#% done < "$CSVFILE"
Command:
$ CSVFILE=test.csv sh -c "$( shtpl interfaces.tpl )"
Result:
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
Enjoy!