Bash Shell: Find Out If a Variable Is Empty Or Not

Posted on in Categories , , , , , , last updated January 21, 2017

My shell script depends upon user input. How do I find out if a variable called $_JAIL path is empty under a Linux / Apple OS X / Unix like operating systems?

Pass the -z option to the if command or conditional expression. If the length of STRING is zero, $var is empty.

Fig.01: Bash scripting various way to determine if a variable is empty
Fig.01: Bash scripting various way to determine if a variable is empty

To find out if a bash variable is empty:

  1. Return true if a bash variable is unset or set to the empty string: if [ -z "$var" ];
  2. Another option: [ -z "$var" ] && echo "Empty"
  3. Determine if a bash variable is empty:[[ ! -z "$var" ]] && echo "Not empty" || echo "Empty"

Let us see syntax and examples in details. The syntax is as follows for if command:

if [ -z "$var" ]
      echo "\$var is empty"
      echo "\$var is NOT empty"

Another option

You can also try the control operators. The syntax is:

[ -z "$var" ] && echo "Empty"
[ -z "$var" ] && echo "Empty" || echo "Not empty"


[[ -z "$var" ]] && echo "Empty"
[[ -z "$var" ]] && echo "Empty" || echo "Not empty"


## Check if $var is set using ! i.e. check if expr is false ##
[ ! -z "$var" ] || echo "Empty"
[ ! -z "$var" ] && echo "Not empty" || echo "Empty"
[[ ! -z "$var" ]] || echo "Empty"
[[ ! -z "$var" ]] && echo "Not empty" || echo "Empty"

For example:

## taken from gen_html_to_pdf() ##
  set -- $line
  local pdfid="$1"
  local pdfurl="$2"
  local pdftitle="$3"
  local pdf="${output}/${pdfid}.pdf"
  [[ -z "$pdfurl" ]] && { echo "Error: URL not found in $_db"; exit 1; }
  echo "Creating $pdf ..."
  # rest of code ...

Sh/Bash one liner examples

Type the following command

## set _JAIL
## find out if it is empty or not ##
[  -z "$_JAIL" ] && echo "Empty: Yes" || echo "Empty: No"

Another example:

[  -z "$_JAIL" ] && echo "Empty: Yes" || echo "Empty: No"

Another example:

## unset (remove) $_JAIL ##
unset _JAIL
[  -z "$_JAIL" ] && echo "Empty: Yes" || echo "Empty: No"

if command syntax and example

if [ -z "$_JAIL" ] 
	echo "Please set \$_JAIL"
	echo "Setting up jail at $_JAIL"
        //call setjail()
        // setjail

A note about double bracket syntax

If portability is not your concern try:

## set _JAIL
## find out if it is empty or not ##
[[ -z "$_JAIL" ]] && echo "Empty" || echo "Not empty"</kbd>


### Warning: Portability issue. ###
## note double bracket syntax:
if [[ -z "$_JAIL" ]]
	echo "Please set \$_JAIL"
	echo "Setting up jail at $_JAIL"
        //call setjail()
        // setjail

Example: Determine if a bash variable is empty

Finally here is a script to demonstrate the various possibilities with bash/sh/posix based shell:

# Do three possibilities for $JAIL ##
for i in 1 2 3 
case $i in
	1) JAIL="/nginx/jail"; HINT="value set";;
	2) JAIL=""; HINT="value set to empty string";;
	3) unset JAIL; HINT="\$JAIL unset";;
# Update user with actual values and action  
# $JAIL set to a non-empty string (1)
# $JAIL set to the empty string  (2)
# $JAIL can be unset (3)
echo "*** Current value of \$JAIL is '$JAIL' ($HINT) ***"
## Determine if a bash variable is empty or not ##
if [ -z "${JAIL}" ]; then
    echo "JAIL is unset or set to the empty string"
if [ -z "${JAIL+set}" ]; then
    echo "JAIL is unset"
if [ -z "${JAIL-unset}" ]; then
    echo "JAIL is set to the empty string"
if [ -n "${JAIL}" ]; then
    echo "JAIL is set to a non-empty string"
if [ -n "${JAIL+set}" ]; then
    echo "JAIL is set, possibly to the empty string"
if [ -n "${JAIL-unset}" ]; then
    echo "JAIL is either unset or set to a non-empty string"

Sample outputs from Bash version 3.2.51:

Fig. 01: Test the various possibilities of variable
Fig. 01: Test the various possibilities of variable

See also

Posted by: Vivek Gite

The author is the creator of nixCraft and a seasoned sysadmin and a trainer for the Linux operating system/Unix shell scripting. He has worked with global clients and in various industries, including IT, education, defense and space research, and the nonprofit sector. Follow him on Twitter, Facebook, Google+.

19 comment

  1. There are a number of advantages of using double brackets for conditionals. One of my favorites is that you do NOT need to use quotes around a variable. It doesn’t matter if it’s empty, has spaces/tabs/newlines in it. You don’t need the quotes.

    1. Double brackets were introduced by David Korn in ksh in the early 80s. Bash was introduced in the late 80’s as an open source shell and incorporated nearly all the features of ksh. I don’t think there are any nix’s that don’t have bash or ksh. I last did serious work on Unix around 2001 till I was forced by circumstances to switch to Linux. I’ve used double brackets since the early 90’s and never had a problem. On many of the Linux system’s I’ve worked on, sh was a symlink to bash. When bash is started as “sh”, it behaves differently (sort of brain dead), but I think it still supports the double bracket. It’s hard to know, since by convention, shell scripts have the “shebang” as the first line (#!/bin/bash).

      1. By ‘bashism’ I meant “it is not ‘sh’ syntax”. It would mostly work on “advanced” ‘sh’ compliant shells.

        As you can see, in the second example, the interpreter is marked as “bin/sh” and the file contains “double square brackets” syntax, which could fail on so many different oses.

        Many non-Linux nixes have only sh (which may or may not be some variant of ksh on solaris, others, I dunno). My recent installation of PCBSD has only csh installed by default. (yuck!)
        Many-a-times, AIX installations do not have bash/ksh/whatever by default. Only sh is installed.

        All I was saying was … the sentence should clearly state why the “double-square-bracket” syntax could fail, rather than just mentioning “portability”.

        *** I know how to install bash on all the platforms, discussing that is not necessary.

  2. When bash is started as sh, the only difference is in which startup files it reads. Its features are unchanged.

    The shebang is unnecessary unless the script uses syntax that the default shell doesn’t understand. And if you use a non-standard shell, you can’t be sure where it is. I use systems where bash is either /bin/bash, /usr/bin/bash, /usr/local/bin/bash or /usr/xpg4/bin/bash. You may be safe with “#!/usr/bin/env bash” but I have used systems where env is in /bin, not /usr/bin.

    I don’t use double brackets. The only thing they give that you can’t do without them is comparing a string against a regular expression (and I have very little need for that).

  3. The shebang saves a bit of time. (It tells the shell what to use to run the program. Otherwise, it has to figure it out. It forks itself as the last resort.) I’ve been using Linux almost exclusively for the last ten or so years, but have occasionally used other ‘nix’s (most recently AIX). I’ve not had a problem with the location of bash.

    There are several reasons Korn added double bracket. If you remember your history, the original condition checker was “test”, located in /bin. Then came “[…]”. Actually ‘[‘ was a hard link to /bin/test. After a while, these were converted to built-ins, but due to backward compatiblity requirements, no advantage could be taken of this (other than improved performance).

    Korn introduced double brackets to get around the backward-compatibility requirement. The problem with single bracket is that all the command line parsing is completed before the condition is tested. That’s why quotes are often necessary.

    If you use “type [” you will see “[ is a shell builtin”. Builtins act like programs. If you use “type [[“, you will see “[[ is a shell keyword”. The difference is that the shell sees the keyword and knows what’s going on. It knows that $var is a variable. And that’s why variables need not be quoted. Ever. Further, double bracket knows what “||” and “&&” are. So, you don’t need to use “-o” and “-a”. Example:
    [[ -f $File && -r $File ]] || echo “‘Not a readable file: ‘$File'”

    Finally, as noted before, you can compare strings (or variable) to shell patterns (not regular expressions). This can be truly useful. For example:
    [[ $S3Msg = ERROR:* ]] && S3Stat=1

    A friendly vendor gave me “The Korn Shell” by David Korn in the early nineties. I’ve been forever grateful. I’ve adopted all the new features, and (due to other things), got as much as a 200 times performance improvement over the old Bourne Shell. When I was forced to move to bash (because pdksh was so buggy), I soon figured out that the minor incompatibilities in no way reduced the functionality of bash. I’ve learned to love it.

  4. You can compare strings or variables against a pattern without using double brackets;use a case statement:

    case $var in
       [a-m]*) echo "First half of alphabet" ;;
       [n-z]*) echo "second half of alphabet" ;;

    You don’t need double brackets to use && or || with test:

    if [ "$x" = 1 ] && [ "$y" = 2 ] || [ "$z" != 3 ]

    Having to quote variables is not a problem; not doing it leads to bad habits.

  5. Using a case statement is a lot of trouble to avoid writing a compound conditional. And don’t get me started on case statements without the leading paren in the condition statements!

    The second form, where you put “&&” and “||” between the condition checks is actually worse than just using -a and -o. And to what purpose? Double bracket solves the problem!

    The comment on “bad habits” … what can I say? I’ve heard people say the same thing about always using braces in their variables: ${x}. If you know what you are doing, you know when braces are necessary. You know when quotes are necessary (or desireable). Certainly, you get different results between ‘echo $x’ and ‘echo “$x”‘. If you know what you are doing, you know which is correct for the situation.

  6. That’s the point. Not only is a case statement no trouble (as you say), double bracket is even less than no trouble. We regard to portability, I’ve been doing shell programming since 85. There were a LOT of issues with Bourne Shell on all the different ‘nix’s that I worked with. (They were “compatible”. NOT!) They claimed to obey the same rules, but the rules were incomplete, and there were differences in limitations. For example, on Ultrix, you were limited to 20 levels of recursion — and that was a BEAR to debug! The solution(s) were to (a) try to figure out a subset of stuff that worked; and (b) try to find a shell on each flavor of Unix (MIPS, IBM, DEC, SCO, DG, NCR, ETC) that would always work (and to write some clever code that could determine what machine you were on and re-launch the script with that shell). I found ksh this way.

    Then this DG guy told me that ksh was now on all Unixes. And he gave me “The Korn Shell”. That was around 1993. I looked on all the machines in house (list above) and found that it was true. I read the book and wrote pages of notes on what could be done and how that would help. For example, I found that using ((Idx = 0; Idx < $X; Idx++)) was roughly 200 times faster than using "Idx=`expr $Idx + 1`" in a loop and then testing $Idx -ge $X.

    So, just how "non-portable" is double bracket? It's portable. With an exception. At some point I ran into a Linux that uses "dash" to run the startup scripts. Dash is a minimal shell, but it's very fast. If you write anything that will execute on startup, you should take the precaution of putting the shebang in the first line. (Should always be there anyway … ya never know when someone will launch your script with zsh or some other odd duck. It's faster, too.)

    I found that the pattern matching ability of double-bracket was roughly 200 times faster than echoing a variable through grep and testing the result.

    I found that "$(< file)" was roughly 200 times faster than `cat file`. And I found a great deal of advantage in being able to nest $(…). (You can nest backticks, but you have to backslash 'em.) This, by the way, is why Korn introduced leading (left) parens in the condition tests of case statements. It let you embed case statements in $(…) command line substitutions. That is, it lets you keep your parens balanced.

    You asked why "&&" is worse than -a. It's all context. With double brackets, you use "&&" and "||" to express a compound condition. With single brackets, you use "-a" and "-o" for the same purpose. It can be a bit trickier, because the shell interprets the line before the condition check takes place. Regardless, these are equivalents.

    Where I said "&&" is worse than -a, I was pointing out that the example was avoiding compound conditionals by making the simple conditionals and using "&&"/"||" between them.

    I don't read posix standard. That's because I'm the victim here. I use what the gods give me. I'm gonna guess, however, that -a and -o are deprecated because double bracket is the standard.

  7. “Portable” means complying with the POSIX standard.

    The Bourne shell could not be counted on to be the same on all systems; the POSIX shell can.

    [[ … ]] cannot be counted on; [ … ] can, and is (with help from case) adequate for almost all needs.

    And, no, -a and -o are not deprecated because [[ … ]] is standard; it’s NOT. They are deprecated because they can lead to confusion and are not reliable in all cases. Where thare are more than 4 elements in a test expression, the result is undefined and cannot be depended on. Multiple tests separated by && or || always work.

    The double brackets are insignificantly faster than single brackets, nowhere near 200 times.

    Dash/ash is a POSIX shell; if you write POSIX-compliant scripts, they will run on dash — as well as ksh and bash.


      The syntax examples at the top of this article are all backwards. They should be as follows:

      [ -z “$var” ] && echo “Empty”
      [[ -z “$var” ]] && echo “Empty”
      # Check if $var is set using ! i.e. check if expr is false ##
      [ ! -z “$var” ] || echo “Empty”
      [[ ! -z “$var” ]] || echo “Empty”

      Also, the one liner example is confusing and backwards. The following is correct:

      ## Is it empty?
      [ -z “$_JAIL” ] && echo “Yes” || echo “No”

Leave a Comment