188

I want to call a settings file for a variable. How can I do this in Bash?

The settings file will define the variables (for example, CONFIG.FILE):

production="liveschool_joe" playschool="playschool_joe" 

And the script will use these variables in it:

#!/bin/bash production="/REFERENCE/TO/CONFIG.FILE" playschool="/REFERENCE/TO/CONFIG.FILE" sudo -u wwwrun svn up /srv/www/htdocs/$production sudo -u wwwrun svn up /srv/www/htdocs/$playschool 

How can I get Bash to do something like that? Will I have to use AWK, sed, etc.?

9 Answers 9

306

The short answer

Use the source command.


An example using source

For example:

config.sh

#!/usr/bin/env bash production="liveschool_joe" playschool="playschool_joe" echo $playschool 

script.sh

#!/usr/bin/env bash source config.sh echo $production 

Note that the output from sh ./script.sh in this example is:

~$ sh ./script.sh playschool_joe liveschool_joe 

This is because the source command actually runs the program. Everything in config.sh is executed.


Another way

You could use the built-in export command and getting and setting "environment variables" can also accomplish this.

Running export and echo $ENV should be all you need to know about accessing variables. Accessing environment variables is done the same way as a local variable.

To set them, say:

export variable=value 

at the command line. All scripts will be able to access this value.

Sign up to request clarification or add additional context in comments.

6 Comments

Must config.sh have execute permission for this to work?
and to restore arrays, you have to store each array entry value separatedly (not the full array single line from declare -p), would be like someArray[3]="abc", and so on...
@Ramiro no it doesn't. I checked. :)
In my case source command throws an error, depends on which shell is being used apparently - stackoverflow.com/a/13702876/2223138
It might be worth noting, that source is a synonym for POSIX . in bash. If you aim for portability of your scrip you could use the POSIX dot.
|
28

Even shorter using the dot (sourcing):

#!/bin/bash . CONFIG_FILE sudo -u wwwrun svn up /srv/www/htdocs/$production sudo -u wwwrun svn up /srv/www/htdocs/$playschool 

2 Comments

When using this in a script, the shorthand is unnecessary and might be confusing. Why not use the full source command to make it clear?
@Lyle Because you want your script to not gratuitously deviate from portable POSIX syntax when you don't have to?
17

Use the source command to import other scripts:

#!/bin/bash source /REFERENCE/TO/CONFIG.FILE sudo -u wwwrun svn up /srv/www/htdocs/$production sudo -u wwwrun svn up /srv/www/htdocs/$playschool 

Comments

12

in Bash, to source some command's output, instead of a file:

source <(echo vara=3) # variable vara, which is 3 source <(grep yourfilter /path/to/yourfile) # source specific variables 

reference

Comments

12

I have the same problem specially in case of security and I found the solution here.

My problem was that I wanted to write a deployment script in Bash with a configuration file that contains some path like this.

################### Configuration File Variable for deployment script ############################## VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0" VAR_CONFIG_FILE_DIR="/home/erman/config-files" VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh" 

An existing solution consists of use "SOURCE" command and import the configuration file with these variables. 'SOURCE path/to/file'

But this solution has some security problems, because the sourced file can contain anything a Bash script can.

That creates security issues. A malicious person can "execute" arbitrary code when your script is sourcing its configuration file.

Imagine something like this:

 ################### Configuration File Variable for deployment script ############################## VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0" VAR_CONFIG_FILE_DIR="/home/erman/config-files" VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"; rm -fr ~/* # hey look, weird code follows... echo "I am the skull virus..." echo rm -fr ~/* 

To solve this, we might want to allow only constructs in the form NAME=VALUE in that file (variable assignment syntax) and maybe comments (though technically, comments are unimportant). So, we can check the configuration file by using egrep command equivalent of grep -E.

This is how I have solve the issue.

configfile='deployment.cfg' if [ -f ${configfile} ]; then echo "Reading user configuration...." >&2 # check if the file contains something we don't want CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(\`]*$)" if egrep -q -iv "$CONFIG_SYNTAX" "$configfile"; then echo "The configuration file is unclean. Please clean it..." >&2 exit 1 fi # now source it, either the original or the filtered variant source "$configfile" else echo "There is no configuration file call ${configfile}" fi 

5 Comments

I haven't verified your syntax checker to make sure it accounts for all cases correctly, but this is by far the best idea because of the security issue.
That's not secure enough, you can still do CMD="$(rm -fr ~/*)".
Thanks @svlasov, I think it's a serious issue, I edit my answer to avoid that type of problem by rejecting characters used for command subtitution like ( and ``` the final CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(`]*$)"
Still not enough. foo=bar unleash_virus can be executed. Note foo=bar\ unleash_virus, foo="bar unleash_virus" and foo=bar #unleash_virus are safe. It's not easy to sanitize properly and not to block some harmless syntax anyway, especially when you think of every possible quoting and escaping.
Updated this to not block arrays? CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(`]*$|[a-z_][^[:space:]]*\+?=\([^;&\(`]*\)$)"
3

Converting a parameter file to environment variables

Usually I go about parsing instead of sourcing, to avoid complexities of certain artifacts in my file. It also offers me ways to specially handle quotes and other things. My main aim is to keep whatever comes after the '=' as a literal, even the double quotes and spaces.

#!/bin/bash function cntpars() { echo " > Count: $#" echo " > Pars : $*" echo " > par1 : $1" echo " > par2 : $2" if [[ $# = 1 && $1 = "value content" ]]; then echo " > PASS" else echo " > FAIL" return 1 fi } function readpars() { while read -r line ; do key=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\1/') val=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\2/' -e 's/"/\\"/g') eval "${key}=\"${val}\"" done << EOF var1="value content" var2=value content EOF } # Option 1: Will Pass echo "eval \"cntpars \$var1\"" eval "cntpars $var1" # Option 2: Will Fail echo "cntpars \$var1" cntpars $var1 # Option 3: Will Fail echo "cntpars \"\$var1\"" cntpars "$var1" # Option 4: Will Pass echo "cntpars \"\$var2\"" cntpars "$var2" 

Note the little trick I had to do to consider my quoted text as a single parameter with space to my cntpars function. There was one extra level of evaluation required. If I wouldn't do this, as in option 2, I would have passed two parameters as follows:

  • "value
  • content"

Double quoting during command execution causes the double quotes from the parameter file to be kept. Hence the 3rd Option also fails.

The other option would be of course to just simply not provide variables in double quotes, as in option 4, and then just to make sure that you quote them when needed.

Just something to keep in mind.

Real-time lookup

Another thing I like to do is to do a real-time lookup, avoiding the use of environment variables:

lookup() { if [[ -z "$1" ]] ; then echo "" else ${AWK} -v "id=$1" 'BEGIN { FS = "=" } $1 == id { print $2 ; exit }' $2 fi } MY_LOCAL_VAR=$(lookup CONFIG_VAR filename.cfg) echo "${MY_LOCAL_VAR}" 

Not the most efficient, but with smaller files works very cleanly.

Comments

2

If the variables are being generated and not saved to a file you cannot pipe them in into source. The deceptively simple way to do it is this:

some command | xargs 

Comments

-1

For preventing naming conflicts, only import the variables that you need:

variableInFile () { variable="${1}" file="${2}" echo $( source "${file}"; eval echo \$\{${variable}\} ) } 

Comments

-2

The script containing variables can be executed imported using Bash.

Consider the script-variable.sh file:

#!/bin/sh scr-var=value 

Consider the actual script where the variable will be used:

 #!/bin/sh bash path/to/script-variable.sh echo "$scr-var" 

3 Comments

This doesn't work, it will run the Bash script in a subprocess and then lose any environment (including variables) it created during its lifetime. Also src-var is not a valid identifier.
What do you mean by "executed imported"? Or "executed and imported"? Or something else?
OK, the OP has left the building ("Last seen more than 2 years ago ").

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.