views:

232

answers:

2

Is there more elegant way of doing lazy evaluation than the following:

pattern='$x and $y'
x=1
y=2
eval "echo $pattern"

results:

1 and 2

It works but eval "echo ..." just feels sloppy and may be insecure in some way. Is there a better way to do this in Bash?

A: 

You're right, eval is a security risk in this case. Here is one possible approach:

pattern='The $a is $b when the $z is $x $c $g.'    # simulated input from user (use "read")
unset results
for word in $pattern
do
    case $word in
        \$a)
            results+=($(some_command))   # add output of some_command to array (output is "werewolf"
            ;;
        \$b)
            results+=($(echo "active"))
            ;;
        \$c)
            results+=($(echo "and"))
            ;;
        \$g)
            results+=($(echo "the sky is clear"))
            ;;
        \$x)
            results+=($(echo "full"))
            ;;
        \$z)
            results+=($(echo "moon"))
            ;;
          *)
            do_something    # count the non-vars, do a no-op, twiddle thumbs
            # perhaps even sanitize %placeholders, terminal control characters, other unwanted stuff that the user might try to slip in
            ;;
    esac
done
pattern=${pattern//\$[abcgxz]/%s}    # replace the vars with printf string placeholders
printf "$pattern\n" "${results[@]}"  # output the values of the vars using the pattern
printf -v sentence "$pattern\n" "${results[@]}"  # put it into a variable called "sentence" instead of actually printing it

The output would be "The werewolf is active when the moon is full and the sky is clear." The very same program, if the pattern is 'The $x $z is out $c $g, so the $a must be $b.' then the output would be "The full moon is out and the sky is clear, so the werewolf must be active."

Dennis Williamson
+1  A: 

Your use of eval is correct, in fact it's a pretty common pattern in shell programming. eval may be a security risk, indeed, but only if the script gains privileges or the data comes from an untrusted source. I imagine that's not your case, right?

By the way, you can also write this:

pattern='$x and $y'
x=1
y=2
eval "result=\"$pattern\""
tokland
I think the security stuff is a bit paranoid for this case. I like the extra quotes.
User1