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

by on December 6, 2012 · 19 comments· LAST UPDATED March 12, 2014

in , ,

My shell script depends upon user input. How do I find out if a variable called $_JAIL path is empty under 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. The syntax is:

Tutorial details
DifficultyEasy (rss)
Root privilegesNo
RequirementsNone
Estimated completion time2m

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

You can also try the control operators:
[ -z "$var" ] && echo "Empty"
[ -z "$var" ] && echo "Empty" || echo "Not empty"

OR

[[ -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"
 

Sh/Bash one liner examples

Type the following command

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

Another example:

_JAIL=""
[  -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

#!/bin/sh
_JAIL="$1"
if [ -z "$_JAIL" ]
then
	echo "Please set \$_JAIL"
 
else
	echo "Setting up jail at $_JAIL"
        //call setjail()
        // setjail
fi

A note about double bracket syntax

If portability is not your concern try:

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

OR

#!/bin/bash
# Warning: Portability issue. 
_JAIL="$1"
## note double bracket syntax:
if [[ -z "$_JAIL" ]]
then
	echo "Please set \$_JAIL"
 
else
	echo "Setting up jail at $_JAIL"
        //call setjail()
        // setjail
fi

Example: Determine if a bash variable is empty

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

 
#!/bin/bash
JAIL="/nginx"
HINT=""
# Do three possibilities for $JAIL ##
for i in 1 2 3
do
case $i in
	1) JAIL="/nginx/jail"; HINT="value set";;
	2) JAIL=""; HINT="value set to empty string";;
	3) unset JAIL; HINT="\$JAIL unset";;
esac
 
###############################################
# 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"
fi
if [ -z "${JAIL+set}" ]; then
    echo "JAIL is unset"
fi
if [ -z "${JAIL-unset}" ]; then
    echo "JAIL is set to the empty string"
fi
if [ -n "${JAIL}" ]; then
    echo "JAIL is set to a non-empty string"
fi
if [ -n "${JAIL+set}" ]; then
    echo "JAIL is set, possibly to the empty string"
fi
if [ -n "${JAIL-unset}" ]; then
    echo "JAIL is either unset or set to a non-empty string"
fi
done
 

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
TwitterFacebookGoogle+PDF versionFound an error/typo on this page? Help us!

{ 19 comments… read them below or add one }

1 Mike Steele December 7, 2012 at 3:32 am

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.

Reply

2 Chris F.A. Johnson December 7, 2012 at 5:37 am

[ -n "$var" ] || echo EMPTY

Reply

3 c0t0r November 24, 2013 at 11:22 am

yes, that’s right

Reply

4 Shantanu Gadgil December 7, 2012 at 6:35 am

Hi,

AFAIK ,the ‘double square brackets’ is bashism and would not work in ‘sh’

The above could be working if ‘sh’ is symlinked to ‘bash’ ?!?

Reply

5 Mike Steele December 7, 2012 at 7:17 am

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).

Reply

6 Shantanu Gadgil December 10, 2012 at 1:38 pm

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.

Reply

7 nixCraft December 10, 2012 at 4:39 pm

The example in question has been updated.

Reply

8 emi2fast December 7, 2012 at 8:14 am

[[ $N ]] || echo “Empty”

Reply

9 Chris F.A. Johnson December 7, 2012 at 8:28 am

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).

Reply

10 Mike Steele December 7, 2012 at 5:45 pm

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.

Reply

11 Chris F.A. Johnson December 7, 2012 at 5:53 pm

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" ;;
esac

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

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

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

Reply

12 Mike Steele December 7, 2012 at 6:03 pm

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.

Reply

13 Chris F.A. Johnson December 7, 2012 at 6:11 pm

A case statement is no trouble and is much more flexible than [[ ... ]]. And it’s portable; [[ ... ]] is not.

Why is && worse than -a?

The use of -a and -o is deprecated in the POSIX standard.

Reply

14 Mike Steele December 8, 2012 at 8:17 am

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.

Reply

15 Chris F.A. Johnson December 8, 2012 at 8:34 am

“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.

Reply

16 Laaa November 24, 2013 at 12:20 am

[ -z "$var" ] || echo “Empty”

there seems something wrong because this code says empty when it is not and outputs nothing if var is unset.

Reply

17 Chris F.A. Johnson November 24, 2013 at 8:32 am

[ -z "$var" ] succeeds if $var is empty.

Reply

18 Jared Wyatt March 12, 2014 at 5:03 pm

NOTE: THE EXAMPLES IN THIS ARTICLE ARE NOT CORRECT.

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”
“`

Reply

19 Nix Craft March 12, 2014 at 6:01 pm

Thanks for the heads up!

Reply

Leave a Comment

Tagged as: , , , ,

Previous Faq:

Next Faq: