493

I am writing a shell script. In this shell script, I am have a variable that either takes a default value, or the value of an environment variable. However, the environment variable doesn't have to be present.

For instance, assume, before running the script, I perform the following operation:

export DEPLOY_ENV=dev

How do I tell the script to search for this environment variable, and store its value in a variable inside the script. Moreover, how do I tell the script that if this environment variable does not exist, store a default variable?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Nicolas El Khoury
  • 5,867
  • 4
  • 18
  • 28
  • 7
    `printenv DEPLOY_ENV` should do it – sjsam Sep 02 '16 at 15:57
  • I tried, for testing purposes tempV=$(printenv LANG) it did not print anything, despite the system having the value of LANG – Nicolas El Khoury Sep 02 '16 at 15:58
  • Your statement doesn't "print" anything. It stores the value of the environment variable `LANG` into the shell variable `tempV`. As for the other suggestions in this thread: Note that with none of them, you can distinguish, whether you have an environment variable or a shell variable. If this distinction is really important, the solution suggested by @sjsam should be considered. – user1934428 Sep 03 '16 at 06:17

5 Answers5

800

[ -z "${DEPLOY_ENV}" ] checks whether DEPLOY_ENV has length equal to zero. So you could run:

if [[ -z "${DEPLOY_ENV}" ]]; then
  MY_SCRIPT_VARIABLE="Some default value because DEPLOY_ENV is undefined"
else
  MY_SCRIPT_VARIABLE="${DEPLOY_ENV}"
fi

# or using a short-hand version

[[ -z "${DEPLOY_ENV}" ]] && MyVar='default' || MyVar="${DEPLOY_ENV}"

# or even shorter use

MyVar="${DEPLOY_ENV:-default_value}"
Bonifacio2
  • 3,405
  • 6
  • 34
  • 54
endurogizer
  • 8,116
  • 1
  • 9
  • 5
  • 34
    You can replace `"${DEPLOY_ENV}"` with `$DEPLOY_ENV` – Finesse May 01 '18 at 04:02
  • 17
    `MyVar="$DEPLOY_ENV:-default_value"` didn't work for me though on bash, so it's generally safer to surround with curly braces `${...}` where possible. – robinmitra May 15 '18 at 07:42
  • 10
    the 'or even shorter use' made my day! – Saber Aug 01 '18 at 17:06
  • The remove curly brackets thing was not for the this proposal but only for when you use the variable directly without the additional expansion – Nicolas Massart Oct 12 '18 at 10:44
  • 3
    This works and may have value in edge cases, but it's a re-invented wheel... Instead, see the parameter "expansion answer", which was made for this exact goal. – Scott Prive Feb 22 '19 at 20:03
  • 13
    @Finesse `Always quote a tested string.` https://www.tldp.org/LDP/abs/html/comparison-ops.html – Andrea Bergonzo Nov 08 '19 at 00:39
  • 1
    This will error if you use `set -euo pipefail`. You need to use @Eugene method of default values first before the `-z` test `MyVar=${DEPLOY_ENV:-}` – shadowbq Jan 29 '20 at 13:54
  • The reason I don't like this solution is that it does check if variable exist or not but I dont want it to check if variable is empty as well. For example, if variable abc="" , the -z test will say abc is empty but I don't care that. – ToiletGuy Feb 11 '21 at 06:52
  • There is even a better syntax : `MyVar = ${DEPLOY_ENV:-default}` – Vinny Jun 08 '21 at 20:40
  • If an env var has a valid empty string value, this is answer not quite correct as the value is set, but has a zero-length value. These are not the same. See Eugene's answer. – David Wood May 05 '23 at 21:54
  • I'm getting an error: "unbound variable" when the var is not set. – Jonny Aug 23 '23 at 08:43
188

You could just use parameter expansion:

${parameter:-word}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

So try this:

var=${DEPLOY_ENV:-default_value}

There's also the ${parameter-word} form, which substitutes the default value only when parameter is unset (but not when it's null).

To demonstrate the difference between the two:

$ unset DEPLOY_ENV
$ echo "'${DEPLOY_ENV:-default_value}' '${DEPLOY_ENV-default_value}'"
'default_value' 'default_value'
$ DEPLOY_ENV=
$ echo "'${DEPLOY_ENV:-default_value}' '${DEPLOY_ENV-default_value}'"
'default_value' ''
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 1
    My script was not able to read the value of DEPLOY_ENV, In order to be sure that the environment variable exists in the system, I wrote printenv DEPLOY_ENV in the terminal, and the correct value was returned. However, the script was not able to fetch it. – Nicolas El Khoury Sep 02 '16 at 16:08
115

If you don't care about the difference between an unset variable or a variable with an empty value, you can use the default-value parameter expansion:

foo=${DEPLOY_ENV:-default}

If you do care about the difference, drop the colon

foo=${DEPLOY_ENV-default}

You can also use the -v operator to explicitly test if a parameter is set.

if [[ ! -v DEPLOY_ENV ]]; then
    echo "DEPLOY_ENV is not set"
elif [[ -z "$DEPLOY_ENV" ]]; then
    echo "DEPLOY_ENV is set to the empty string"
else
    echo "DEPLOY_ENV has the value: $DEPLOY_ENV"
fi
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 9
    Note that the `-v` option was introduced in Bash 4.2, and many systems are still running older versions of Bash. – pjh Sep 02 '16 at 17:22
  • 21
    Even `bash` 4.2 is over 5 years old at this point; I tend to treat anything older as a special case that deserves special mention in the question. – chepner Sep 02 '16 at 17:33
  • 5
    It is amazing how time flies... – David C. Rankin Sep 02 '16 at 18:43
  • This code gives me `./deploy.sh: line 3: conditional binary operator expected` - at least on mac for `if [[ ! -v DEPLOY_ENV ]]; then` – fIwJlxSzApHEZIl Apr 21 '17 at 00:00
  • 6
    @anon58192932 That's because you are using `/bin/bash`, which is only version 3.2.x on Mac OS X. – chepner Apr 21 '17 at 00:01
  • 7
    the list of reasons to switch to linux gets another entry... thanks @chepner – fIwJlxSzApHEZIl Apr 21 '17 at 00:07
  • 1
    You can install a new version yourself. – chepner Apr 21 '17 at 01:51
  • beware that "-v" might fail test for hashes and arrays. It returns false unless the variable has an element of key/indice "0". Also it doesn't work for special parameters like $1, $-, $#... Use declare -p / typeset -p instead! – Dmitry Shevkoplyas Sep 30 '19 at 15:33
  • 1
    @DmitryShevkoplyas `-v` has worked for arrays (both indexed and associative) since `bash` 4.2. The special parameters are either always set, or indeed handled differently. (For example, test the value of `$#` to see which positional parameters are actually set.) – chepner Sep 30 '19 at 15:35
  • macos even in 2020 (Catalina) uses GPLv2 bash3.x, you need to account for that or the user needs to `brew install bash`, and your scripts should use `/usr/bin/env bash` – shadowbq Jan 29 '20 at 13:58
  • Don't use `/usr/bin/env bash`, unless you are the only one using the script. The version of `bash` your script requires may not be the same version that `env` will run for someone else. – chepner Jan 29 '20 at 14:34
  • In POSIX `[[` is undefined but the fix is easy, just use a single bracket `[` instead. – Gunar Gessner Jul 14 '21 at 09:30
  • @GunarGessner POSIX `[` doesn't have a `-v` operator, so in this context just using `[` isn't a solution. – chepner Nov 17 '22 at 20:25
60

There is no difference between environment variables and variables in a script. Environment variables are just defined earlier, outside the script, before the script is called. From the script's point of view, a variable is a variable.

You can check if a variable is defined:

if [ -z "$a" ]
then
    echo "not defined"
else 
    echo "defined"
fi

and then set a default value for undefined variables or do something else.

The -z checks for a zero-length (i.e. empty) string. See man bash and look for the CONDITIONAL EXPRESSIONS section.

You can also use set -u at the beginning of your script to make it fail once it encounters an undefined variable, if you want to avoid having an undefined variable breaking things in creative ways.

Robert
  • 7,394
  • 40
  • 45
  • 64
  • 1
    "There is no difference between environment variables and variables in a script." -- to clarify: this is not true in the important sense that, while an environment variable is also a shell variable, a shell variable is not always exported as an environment variable. Here are some ways to tell if a variable is exported: https://superuser.com/questions/450868/what-is-the-simplest-scriptable-way-to-check-whether-a-shell-variable-is-exporte – Croad Langshan Jul 16 '22 at 13:52
7
NEW_VAR=""
if [[ ${ENV_VAR} && ${ENV_VAR-x} ]]; then
  NEW_VAR=${ENV_VAR}
else
  NEW_VAR="new value"
fi