3

I read a string from a file with shell script variables, and want to substitute the variables with values in a function like

hello.txt:
-------------
Hello $NAME


a.sh 
-------------
function printout
{
  echo ???somehow_parse??? $1
}

NAME=Joe
printout "$(cat hello.txt)"
NAME=Nelly
printout "$(cat hello.txt)"

The example is not the best, but it describes my problem. In other words: can I use shell as a template engine?

I am using ksh.

torbatamas
  • 1,236
  • 1
  • 11
  • 21
  • possible duplicate of [Lazy Evaluation in Bash](http://stackoverflow.com/questions/2900707/lazy-evaluation-in-bash) – Shawn Chin Feb 03 '12 at 14:38

4 Answers4

1

If you're sure that the contents of your template file is completely safe, that is, it doesn't contain a string to execute a command that might harm your computer, then you can use eval:

#!/bin/bash
NAME=Joe
TEMPLATE=$(cat hello.txt)
eval "echo $TEMPLATE"
NAME=Nelly
eval "echo $TEMPLATE"

Example output:

HELLO Joe
HELLO Nelly
jcollado
  • 39,419
  • 8
  • 102
  • 133
1

In general, I would go for a search-and-replace approach using sed/awk such as that shown in Kent's answer or this answer.

If you want a shell-only approach, then the standard way would be to use eval. However, this poses a security risk. For example:

[me@home]$ cat hello.txt
hello $NAME; uname -a
[me@home]$ NAME="shawn"
[me@home]$ eval echo "`cat hello.txt`"   # DO NOT DO THIS!
hello shawn
Linux SOMEHOST 2.6.9-101.ELsmp #1 SMP Fri May 27 18:57:30 EDT 2011 i686 i686 i386 GNU/Linux

Notice how a command can be injected into the template!

You can however reduce the risk using this approach:

[me@home]$ eval "OUT=\"`cat hello.txt`\""
[me@home]$ echo $OUT
hello shawn; uname -a

Do note that this is still not foolproof as commands can still be injected using $(cmd) or `cmd`.

In short, you should use eval only if you understand the risks and can control/limit access to the template files.

Here's an example of how this can be applied in your script:

function printout {
    FILENAME=$1
    eval "OUT=\"`cat $FILENAME`\""
    echo $OUT
}

NAME=Joe
printout hello.txt
NAME=Nelly
printout hello.txt
Community
  • 1
  • 1
Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
  • This `eval` was what I wanted. It is sure risky, but the only user of my script is me. If I am a fool, shame on me. – torbatamas Feb 03 '12 at 14:42
0

I think the easiest solution is to use the basename utility.

For instance, if you have the following string: a='/home/you/stuff/myfile.txt' you could use commands like:

dirname $a
basename $a
basename $a .txt

and get output that looks like this:

/home/you/stuff
myfile.txt
myfile
john_science
  • 6,325
  • 6
  • 43
  • 60
0

like this?

kent$  head hello.txt t.sh
==> hello.txt <==
hello $name

==> t.sh <==
#!/bin/bash

function printout
{
  echo $1|awk -v name="$name" 'gsub(/\$name/,name)'
}
name=xxx
printout "$(cat hello.txt)"
name=yyy
printout "$(cat hello.txt)"

run it:

kent$  ./t.sh
hello xxx
hello yyy
Kent
  • 189,393
  • 32
  • 233
  • 301