How to check if bash variable defined in script

I need to set a bash environment variable called PURGEIMAGE and take specific actions in my script. For example, if PURGEIMAGE set, then purge images from CDN else just URL. I am going to call my script as PURGEIMAGE=yes ./purgecache.sh url1 url2. So how do I know if a bash variable set at the CLI and used in my bash script running on Linux or Unix-like systems?

We can pass the -z option to the if command or conditional expression to check if a bash variable defined in script or not.
Tutorial details
Difficulty level Easy
Root privileges No
Requirements Bash running on Linux or Unix
Est. reading time 2m

To find out if a bash variable is defined:

  1. Return true if a bash variable is unset or set to the empty string: if [ -z ${my_variable+x} ];
  2. Also try: [ -z ${my_bash_var+y} ] && echo "\$my_bash_var not defined"
  3. Determine if a bash variable is set or not : [[ ! -z ${PURGEIMAGE+z} ]] && echo "Set" || echo "Not defined"
  4. Return true if the variable is set on Bash version 4.2+: [ -v $VAR ] && echo "Bash \$VAR NOT set"

if command syntax and example to see if bash variable named PURGEIMAGE set or not

The syntax is simple:

if [ -z ${PURGEIMAGE+x} ];
then
    echo "We need to only purge URL from Clodudfront CDN ..."
else
    echo "We need to purge images and URL from Cloudfront CDN ..."
fi

OR use the test command

if test -z ${PURGEIMAGE+y}
then
    echo "We need to only purge URL from Clodudfront CDN ..."
else
    echo "We need to purge images and URL from Cloudfront CDN ..."
fi

Then we run our script as
$ /path/to/script
$ PURGEIMAGE=whatever /path/to/script
# use the export command to export variable to sub-shells
$ export PURGEIMAGE=yes
$ /path/to/script

All exported variables can undefined values as follows using the unset command $ unset PURGEIMAGE
$ /path/to/script

How does ${my_variable+x} syntax works?

Let us print commands and their arguments as script is executed by using the set command:

set -x
./script
PURGEIMAGE=whatever ./script
export PURGEIMAGE=whatever
./script
unset PURGEIMAGE
./script
set +x
How to check if bash variable defined in script
  • The ${my_variable+x} is a bash parameter expansion.
  • It evaluates to nothing if my_variable is unset.
  • Otherwise it substitutes the string x.
  • You can use any string and not limited to x. For instance, I can use foobar as follows:
    # instead of 'x' we used 'foobar'
    # but 'x' seems like a good practice 
    if [ -z ${PURGEIMAGE+foobar} ];
    then
        echo "We need to only purge URL from Clodudfront CDN ..."
    else
        echo "We need to purge images and URL from Cloudfront CDN ..."
    fi

Another option to see if bash shell variable is set or not

We can try the control operators. The syntax is:

[ -z ${var+foo} ] && echo "\${var} not defined"
[ -z ${var+foo} ] && echo "\${var} not defined" || echo "\${var} is defined"

If portability is not your concern try [[ syntax

The following are bash specific option, and you need bash version 4.x+. I would avoid these examples due to portability issues if I were you, but I provide them below as they are in bash man pages.

[[ -z ${var+z} ]] && echo "\${var} not defined" 
[[ -z ${var+z} ]] && echo "\${var} not defined" || echo "\${var} is defined"

Checking if a variable is set in Bash or not

The -v VAR returns true if the shell variable VAR is set:

[[ -v ${var} ]] && echo "\${var} not defined" 
[[ -v ${var} ]] && echo "\${var} not defined" || echo "\${var} is defined"
[ -v ${var} ] && echo "\${var} not defined" || echo "\${var} is defined"

Putting it all together

## check bash variable defined or not ##
  if [  -z ${PURGEIMAGE+x} ]
  then
     case "$u" in
	     https://www.cyberciti.biz/faq/*) pattern="media/new/faq/";;
             https://www.cyberciti.biz/tips/*) pattern="/tips/wp-content/uploads/";;
	     https://www.cyberciti.biz/*) pattern="media/new/cms/";;
	     *) pattern="NULL"
     esac
     if [ "$pattern" != "NULL" ]
     then
	     echo ' * Getting a list of images to purge from url ... '
	     tmp_file="/tmp/index.html.$$"
	     wget -o /dev/null -O "$tmp_file" "$u"
	     if [ -f "$tmp_file" ]
	     then
		     img_urls="$img_urls $(grep -i 'img' "$tmp_file"  | grep -Po 'src="\K.*?(?=")' | grep "$pattern" | grep -E -v 'inbox-sign-up.002.jpeg')"
		     rm -f "$tmp_file"
	     fi
     fi
     purge_images "$img_urls"
  fi
  purge_url "$u"

Summing up

CDN like Cloudfront and Cloudflare allows us to cache both images and HTML pages aggressively on the edge node. By caching those assets, we can speed up the website. However, when we updated pages, we need to remove URLs and images from the cache. However, often mages are not required to purge as we only update pages. This simple bash script allows me to purge assets as per our needs on-demand. See:


🐧 Get the latest tutorials on Linux, Open Source & DevOps via RSS feed or Weekly email newsletter.

🐧 4 comments so far... add one

CategoryList of Unix and Linux commands
Disk space analyzersdf ncdu pydf
File Managementcat tree
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network UtilitiesNetHogs dig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
4 comments… add one
  • j Jan 29, 2021 @ 17:02

    Please explain how ${PURGEIMAGE+foobar}is different than ${PURGEIMAGE}? I thought you were trying to say if PURGEIMAGE was not defined is value would be set to foobar but that is not the case. Likewise, I am not seeing the difference in my test scripts if I remove +foobar?

    • 🐧 Vivek Gite Jan 29, 2021 @ 17:44

      Try:

      var=
      [ -z ${var+y} ] && echo "Unset/Blank" || echo "Set"
      [ -z "$var" ] && echo "Unset/Blank" || echo "Set"
      
      var=whatever
      [ -z ${var+y} ] && echo "Unset/Blank" || echo "Set"
      [ -z "$var" ] && echo "Unset/Blank" || echo "Set"
      
      unset var
      [ -z ${var+y} ] && echo "Unset/Blank" || echo "Set"
      [ -z "$var" ] && echo "Unset/Blank" || echo "Set"
      
  • Jay Patel Jan 30, 2021 @ 4:18

    Interesting, I like bash 4.2 solution. Very easy to use

    if [ -v ${var}  ];
    then
       call_var_not_defined_action
    else
       call_var_defined_action
    fi
  • Joe Klemmer Feb 2, 2021 @ 13:42

    It seems that -n works for this, too.

    if [ -n "$VAR" ]; then
        echo "Set"
    else
        echo "Not set"
    fi 
    

    At least it’s working for me.

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum