I have a text file that I'm using as a template, it looks like this:Hostname     : $HOSTNAMEHost Address : $HOSTADDRESSMy bash script sets two variables, HOSTNAME and HOSTADDRESS, reads the template file then does an eval to expand $HOSTNAME and $HOSTADDRESS:HOSTNAME=SH_SQL_0089HOSTADDRESS=172.16.3.44TEMPLATE=`cat template.txt`MESSAGE=`eval echo $TEMPLATE`The resultant value of MESSAGE is:Hostname     : SH_SQL_0089Host Address : 172.16.3.44Is it possible to reduce the last two lines to something like:MESSAGE=$(cat template.txt | eval echo ????)I tried using xargs:MESSAGE=$( cat template.txt | xargs -i bash -c eval echo {} )But it only substitutes $HOSTNAME and strips the carriage returns. The reason I want to do this is that I need to add a third processing step and pipe the eval'd output to that. I feel I'm so close.
Passing pipeline value as parameter to xargs for use by eval echo
bash;pipe;xargs
First, xargs can't work here because the substitutions would be executed in a subprocess (the bash processes that xargs launches). But only environment variables are passed to subprocesses, not shell variables. Evidently HOSTNAME was already in your script's environment when it started, but HOSTADDRESS isn't. At this point you might be tempted to export all variables, but even then xargs isn't a good solution because it suffers from numerous quoting issues  if your template contains \' or if you want to retain whitespace, you're toast.Now, looking at your current code: MESSAGE=`eval echo $TEMPLATE` is a complex way of writing eval MESSAGE=$TEMPLATE, except for quoting issues. And you do have quoting issues; for example you'll have noticed that all your whitespace has been collapsed. You've heard of Bobby Tables, haven't you? Shell expansion rules are pretty complex, but there are some rules that'll keep you sane:Always double quotes around variable substitutions $foo and command substitutions $(bar). You may violate this rule if you understand why you need to omit the quotes.Use $() rather than `` for command substitutions. Quoting inside the backquote form is arcane and not portable, whereas quoting inside $() works normally.Use eval only if pressed at gunpoint. If you do, be very careful, and make it as simple as possible.So what can go wrong with eval MESSAGE=$TEMPLATE? It makes the shell evaluateMESSAGE=Hostname     : $HOSTNAMEHost Address : $HOSTADDRESSOops, we need quotes around the part that should be the value of MESSAGE. The quotes have to get through to eval, so they need to go literally through the first stage of shell expansion: eval MESSAGE=\$TEMPLATE\.MESSAGE=Hostname     : $HOSTNAMEHost Address : $HOSTADDRESSBetter, but now you have exactly the Bobby Tables injection pattern  what if the template contains a quote? Then you need to escape the quotes. The four characters that have a special meaning between double quotes are \$`, and you want $ to retain its meaning, so add a backslash before the other three.TEMPLATE=$(sed -e 's/[\\`]/\\&/g' <template.txt)eval MESSAGE=\$TEMPLATE\Now the shell will evaluateMESSAGE=Hostname     : $HOSTNAMEHost Address : $HOSTADDRESSName         : Bobby \drop\ O'Tablesand all's good.Note that proper quoting here is to protect against accidental parsing problems; whoever controls the template still has shell access (with $(hello)).If you wanted the template included in your shell script, this would be naturally done with a heredoc.MESSAGE=$(cat <<EOF)Hostname     : $HOSTNAMEHost Address : $HOSTADDRESSEOFBut with an external template you'd need to do two steps of evaluation, hence use eval, and bash's parsing is quite buggy when it comes to funky stuff like heredocs inside eval. There's surely a way that works, at least with bash 4, but I don't recommend risking it.