Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Is there an easy way to substitute/evaluate environment variables in a file? Like let's say I have a file config.xml that contains:

<property>
    <name>instanceId</name>
    <value>$INSTANCE_ID</value>
</property>
<property>
    <name>rootPath</name>
    <value>/services/$SERVICE_NAME</value>
</property>

...etc. I want to replace $INSTANCE_ID in the file with the value of the INSTANCE_ID environment variable, $SERVICE_NAME with the value of the SERVICE_NAME env var. I won't know a priori which environment vars are needed (or rather, I don't want to have to update the script if someone adds a new environment variable to the config file). Thanks!

share|improve this question
    
When will you do something with file (cat, echo, source,…) the variable will subtitute by its value – Costas 22 hours ago
    
Is the contents of this xml file up to you? If so, parameterized xslt offers another way to inject values and (unlike envsubst and its ilk) guarantees well formed xml as a result. – kojiro 19 hours ago
up vote 21 down vote accepted

You could use envsubst (part of gnu gettext):

envsubst < infile

will replace all1 environment variables in your file with their corresponding value.


1: one can also replace only certain environment variables, see this question.

share|improve this answer
1  
This seems more secure than the accepted answer. – Rui F Ribeiro 22 hours ago
    
Looks like the exact tool I was looking for; changing to accept this one. – Robert Fraser 22 hours ago
1  
...except it's not installed by default in my docker image :'-( – Robert Fraser 22 hours ago
3  
That's good. Docker images should be lightweight and tailor made. Of course you could always add envsubst to it, though. – kojiro 19 hours ago

If you happen to have Perl (but not gettext and envsubst) you can do the simple replacement with a short script:

$ export INSTANCE_ID=foo; export SERVICE_NAME=bar;
$ perl -pe 's/\$([_A-Z]+)/ $ENV{$1} /e'  < config.xml
<property>
    <name>instanceId</name>
    <value>foo</value>
</property>
<property>
    <name>rootPath</name>
    <value>/services/bar</value>
</property>

I assumed the variable names will only have uppercase letters and underscores, but the first pattern should be easy to alter as needed. /e runs the substitution as a perl expression, and $ENV{...} references the environment Perl sees.

(If you want to support the ${...} syntax or throw an error on unset variables, you'll need some more work.)

Though I feel that feeding variables like that via the process environment seems a bit iffy in general: you can't use arbitrary variables in the files (since they may have special meanings), and some of the values could possibly have at least semi-sensitive data in them.

share|improve this answer
    
Would rather not use Perl since it's supposed to be a docker container, but that looks like the best solution. – Robert Fraser 22 hours ago

This is not very nice but it works

( echo "cat <<EOF" ; cat config.xml ; echo EOF ) | sh

If it was in a shell script it would look like:

#! /bin/sh
cat <<EOF
<property>
    <name>instanceId</name>
    <value>$INSTANCE_ID</value>
</property>
EOF

Edit, second proposal:

eval "echo \"$(cat config.xml)\""
share|improve this answer
    
The problem with this is that if the file contains a line with EOF, the remaining lines will be executed as commands by the shell. We could change the separator to something longer or more complicated, but there's still a theoretical possibility of colliding. And someone could deliberately make a file with the separator to execute commands. – ilkkachu 22 hours ago
    
OK, try this: eval "echo \"$(cat config.xml)\"" – hschou 22 hours ago
    
That seems to work; thanks! – Robert Fraser 22 hours ago
2  
Try putting something like "; ls ;" inside the file and do that eval command again :) This is pretty much the same problem as with SQL injection attacks. You have to be really careful when mixing data with code (and that's what shell commands are), unless you're really, really sure nobody is trying to do anything to mess up your day. – ilkkachu 22 hours ago
2  
@hschou I think ilkkachu meant `"; ls ;"` — the comment formatting ate the backticks. But actually that shoule be just `ls` here. The point is that the content of the file leads to arbitrary code execution and there's nothing you can do about it. – Gilles 14 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.