How do I substitute environment variables when I ouput a file?

7

7

I'd like to somehow spit out a file (which has env variable references) and substitute the actual values. This is what I've tried, but as you can see it's not working. Any ideas?

-bash-3.00$ cat vars_file 
${MY_VAR}
-bash-3.00$ export MY_VAR=MY_VALUE; cat ./vars_file | xargs echo
${MY_VAR}

andersonbd1

Posted 2011-01-20T17:27:45.563

Reputation: 485

Answers

10

Old thread, but this is a recurrent issue. Here is a solution using bash's mapfile:

mateus@mateus:/tmp$ cat input.txt
a = $a
b = $b
mateus@mateus:/tmp$ echo a=$a, b=$b
a=1, b=2
mateus@mateus:/tmp$ function subst() { eval echo -E "$2"; }
mateus@mateus:/tmp$ mapfile -c 1 -C subst < input.txt
a = 1
b = 2

The bash builtin mapfile calls user-defined function subst (see -C/-c options) on each line read from input file input.txt. Since the line contains unescaped text, eval is used tu evaluate it and echo the transformed text to stdout (-E avoid special characters to be interpreted).

IMHO this is a much more elegant than any sed/awk/perl/regex based solution.

Another possible solution is to use shell's own substitution. This looks like a more "portable" way not relaying on mapfile:

mateus@mateus:/tmp$ EOF=EOF_$RANDOM; eval echo "\"$(cat <<$EOF
$(<input.txt)
$EOF
)\""
a = 1
b = 2

Note we use $EOF to minimize conflicting cat's here-document with input.txt content.

Both examples from: https://gist.github.com/4288846

EDIT: Sorry, the first example doesn't handles comments and white space correctly. I'll work on that and post a solution. EDIT2: Fix double $RANDOM eval

caruccio

Posted 2011-01-20T17:27:45.563

Reputation: 118

Have you tested your second solution? Won't each reference to $RANDOM generate possibly different numbers? When I type echo $RANDOM $RANDOM in a terminal, I get two different numbers. – Victor – 2014-11-17T15:00:31.207

You are right. It produces 2 random number and that heredoc will never close. I will edit it to fix. Thanks! – caruccio – 2014-11-17T19:03:01.797

2

Can be done with script (e.g. file name is expand.sh):

while read line; do eval echo \"$line\"; done < $1 > $2

The script may be called like this:

env VAR1=value1 sh expand.sh input_file output_file

http://www.issociate.de/board/post/281806/sed_replace_by_enviroment_var_content.html

Tamara Wijsman

Posted 2011-01-20T17:27:45.563

Reputation: 54 163

any idea how to do this when there's a paren in the file?

-bash: syntax error near unexpected token `(' – andersonbd1 – 2011-01-20T17:51:38.343

@andersonbd1: I suppose you could replace the parentheses with sed and replace them back later. Otherwise, you might want to consider to remove the accept mark from my answer to get a better awk solution, as the above questions mentions that you can't read the effect with sed. – Tamara Wijsman – 2011-01-20T18:09:02.157

1I guess sed is my best bet. It ain't pretty but it'll work. Thanks TomWij – andersonbd1 – 2011-01-20T18:24:15.617

You might want to quote that variable: echo "$line". @andersonbd1: what is the context of the parentheses? – Paused until further notice. – 2011-01-20T19:06:33.283

@Dennis: See the last post of the link; with quotes, weird things happen with backslash sequences. – Tamara Wijsman – 2011-01-20T19:12:38.850

1Oops, sorry, I forgot to say that you should use read -r, too. – Paused until further notice. – 2011-01-20T19:14:01.290

2

As described in the same thread at http://www.issociate.de/board/post/281806/sed_replace_by_enviroment_var_content.html, the "proper" solution would be as follows:

awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < input.txt > output.txt

eval approach is fine, but tends to fail on XML files for instance.

alf

Posted 2011-01-20T17:27:45.563

Reputation: 121

1

Here's my proposed solution:

eval $'cat <<\002\n'"$(<ifile)"$'\n\002' > ofile

\002 can be replaced with any character or string that doesn't occur in the ifile. To delete any occurrences of the delimiting character in the input file:

eval $'cat <<\002\n'"$(tr -d '\002' < ifile)"$'\n\002' > ofile

This solution seems to resolve most issues but is notably vulnerable to command injection in the template file through $(command) directives.

Stefan Farestam

Posted 2011-01-20T17:27:45.563

Reputation: 111

1

$"xxx" may be a easy way

a=$(< file)
eval b=\$\"$a\"

then "$b" is the expanded text

abc

Posted 2011-01-20T17:27:45.563

Reputation: 31

0

This defines a shell function that uses python to mimic basic shell expansion of ${name} variables. I use it to expand bash variables embedded in sql scripts. Since my file is not a true bash file, you cant use the eval() bash builtin

apply_shell_expansion() {
python - << EOF
import os
doc=open('${1}').read()
for k,v in os.environ.items():
  doc = doc.replace('$'+'{'+k+'}',v)
print doc
EOF
}

MARK L HAMMINGH

Posted 2011-01-20T17:27:45.563

Reputation: 1

1(1) What do myvar and myvalue have to do with anything?  (2) You don’t need to say ${1}; you can say $1. – Scott – 2017-10-06T03:25:33.080

removed the myvar=myvalue example as apparently its more confusing than helpful – MARK L HAMMINGH – 2017-10-06T05:00:39.307