[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
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
:
Hello, ${NAME}!
Today, the weather is ${WEATHER}. Enjoy!
Create a configuration file to store your variables. Let's call it 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
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:
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:
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 file and the template file is 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
.
As long as you control the content of these files, it is fine to use this eval
templating.
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 templating is quite famous in Python.