1092

What is the best way to determine if a variable in bash is empty ("")?

I have heard that it is recommended that I do if [ "x$variable" = "x" ]

Is that the correct way? (there must be something more straightforward)

3
  • 1
    See: Test for non-zero length string in Bash on StackOverflow. Commented Jun 10, 2019 at 21:36
  • 3
    In Bash, [[ -v VAR]] is to check if VAR is defined, [[ -z $VAR ]] is to check if "$VAR" is expanded to null string (""). Thus, you can use [[ -v VAR && -z $VAR ]]. read more here (with official reference) Commented Nov 17, 2019 at 7:42
  • related: [[ vs [ Commented Jun 7, 2024 at 11:56

16 Answers 16

1452

This will return true if a variable is unset or set to the empty string ("").

if [ -z "${VAR}" ]; 
10
  • 13
    what's the inverse of -z? if not empty string. Commented Jul 19, 2013 at 23:48
  • 163
    if [ ! -z "$VAR" ]; Commented Jul 22, 2013 at 17:26
  • 392
    the inverse of -z is -n if [ -n "$VAR" ]; Commented Oct 9, 2013 at 5:59
  • 33
    The double quotes ensure that the variable doesn't get split. A simple $var on a command line will be split by its whitespace into a list of parameters, while "$var" will always be only one parameter. Quoting variables often is a good practice and stops you from tripping up on filenames containing whitespace (among other things). Example: after doing a="x --help", try cat $a - it will give you the help page for cat. Then try cat "$a" - it will (usually) say cat: x --help: No such file or directory. In short, quote early and quote often and you will almost never regret it. Commented Jul 15, 2014 at 13:40
  • 3
    I feel like your answer requires a caveat - namely if your bash script has something like set -u then the answer will break/fail at that point. There is a subtle (and usually ignored) difference between unset and empty. Note that the distinction is only really important if it's important to you, or if the script has already set -u. Commented Feb 8, 2017 at 16:01
348

In Bash, when you're not concerned with portability to shells that don't support it, you should always use the double-bracket syntax:

Any of the following:

if [[ -z $variable ]] if [[ -z "$variable" ]] if [[ ! $variable ]] if [[ ! "$variable" ]] 

In Bash, using double square brackets, the quotes aren't necessary. You can simplify the test for a variable that does contain a value to:

if [[ $variable ]] 

This syntax is compatible with ksh (at least ksh93, anyway). It does not work in pure POSIX or older Bourne shells such as sh or dash.

See my answer here and BashFAQ/031 for more information about the differences between double and single square brackets.

You can test to see if a variable is specifically unset (as distinct from an empty string):

if [[ -z ${variable+x} ]] 

where the "x" is arbitrary.

If you want to know whether a variable is null but not unset:

if [[ -z $variable && ${variable+x} ]] 
9
  • 5
    I think this is better than the accepted answer. Commented Apr 5, 2013 at 10:00
  • 5
    Why are you recommending a non-portable feature when doing so gives no benefit? Commented Nov 24, 2014 at 8:16
  • 28
    @AlastairIrvine: I mention portability in the first sentence of my answer, the question title and body contain the word "Bash" and the question is tagged bash, and the double bracket structure provides clear advantages in many ways. And I don't recommend mixing bracket styles for reasons of consistency and maintainability. If you need maximum, lowest common denominator portability, use sh instead of Bash. If you need the increased capabilities that it provides, use Bash and use it fully. Commented Nov 24, 2014 at 13:59
  • 3
    @BrunoBronosky: I reverted the edit. There's no requirement for ; at the end. The then can be on the next line without a semicolon at all. Commented Nov 30, 2016 at 23:52
  • 8
    You can do that, but a puppy dies every time you put then on its own line. Commented Dec 1, 2016 at 3:11
346

A variable in bash (and any POSIX-compatible shell) can be in one of three states:

  • unset
  • set to the empty string
  • set to a non-empty string

Most of the time you only need to know if a variable is set to a non-empty string, but occasionally it's important to distinguish between unset and set to the empty string.

The following are examples of how you can test the various possibilities, and it works in bash or any POSIX-compatible shell:

if [ -z "${VAR}" ]; then echo "VAR is unset or set to the empty string" fi if [ -z "${VAR+set}" ]; then echo "VAR is unset" fi if [ -z "${VAR-unset}" ]; then echo "VAR is set to the empty string" fi if [ -n "${VAR}" ]; then echo "VAR is set to a non-empty string" fi if [ -n "${VAR+set}" ]; then echo "VAR is set, possibly to the empty string" fi if [ -n "${VAR-unset}" ]; then echo "VAR is either unset or set to a non-empty string" fi 

Here is the same thing but in handy table form:

 +-------+-------+-----------+ VAR is: | unset | empty | non-empty | +-----------------------+-------+-------+-----------+ | [ -z "${VAR}" ] | true | true | false | | [ -z "${VAR+set}" ] | true | false | false | | [ -z "${VAR-unset}" ] | false | true | false | | [ -n "${VAR}" ] | false | false | true | | [ -n "${VAR+set}" ] | false | true | true | | [ -n "${VAR-unset}" ] | true | false | true | +-----------------------+-------+-------+-----------+ 

The ${VAR+foo} construct expands to the empty string if VAR is unset or to foo if VAR is set to anything (including the empty string).

The ${VAR-foo} construct expands to the value of VAR if set (including set to the empty string) and foo if unset. This is useful for providing user-overridable defaults (e.g., ${COLOR-red} says to use red unless the variable COLOR has been set to something).

The reason, why [ x"${VAR}" = x ] is often recommended for testing whether a variable is either unset or set to the empty string, is some implementations of the [ command (also known as test) are buggy. If VAR is set to something like -n, then some implementations will do the wrong thing when given [ "${VAR}" = "" ], since the first argument to [ is erroneously interpreted as the -n operator, not a string.

4
  • 3
    Testing for a variable set to the empty string can also be done using [ -z "${VAR-set}" ]. Commented May 17, 2013 at 17:56
  • @nwellnhof: Thanks! I updated my answer to use the briefer syntax. Commented May 18, 2013 at 18:00
  • In bash, there's also a difference between ${VAR+foo} and ${VAR:+foo}. The former would evaluate to foo only if VAR is unset, and the version with : will also return foo if VAR is set to the empty string. Commented Nov 11, 2014 at 19:51
  • 6
    The unset checks aren't reliable. If the user called set -u or set -o nounset in bash, then the test will just result in the error "bash: VAR: unbound variable". See stackoverflow.com/a/13864829 for a more reliable unset check. My go-to check for whether a variable is null or unset is [ -z "${VAR:-}" ]. My check for whether a variable is non-empty is [ "${VAR:-}" ]. Commented Sep 14, 2017 at 14:12
47

-z is a the best way.

Another options I've used is to set a variable, but it can be overridden by another variable eg

export PORT=${MY_PORT:-5432} 

If the $MY_PORT variable is empty, then PORT gets set to 5432, otherwise PORT is set to the value of MY_PORT. Note the syntax include the colon and dash.

1
  • 2
    Accidentally found this today, and it was exactly what I wanted. Thanks! I have to tolerate set -o nounset in some scripts. Commented Feb 16, 2012 at 23:58
25

If you're interested in distinguishing the cases of set-empty versus unset status, look at the -u option for bash:

$ set -u $ echo $BAR bash: BAR: unbound variable $ [ -z "$BAR" ] && echo true bash: BAR: unbound variable $ BAR="" $ echo $BAR $ [ -z "$BAR" ] && echo true true 
11

An alternate I've seen to [ -z "$foo" ] is the following, however I'm not sure why people use this method, anyone know?

[ "x${foo}" = "x" ] 

Anyway if you're disallowing unset variables (either by set -u or set -o nounset), then you'll run into trouble with both of those methods. There's a simple fix to this:

[ -z "${foo:-}" ] 

Note: this will leave your variable undef.

1
  • 5
    There's a comment about the alternative to -z at pubs.opengroup.org/onlinepubs/009695399/utilities/test.html. Basically, it isn't meant to be an alternative to -z. Rather, it handles cases where $foo could expand to something beginning with a metacharacter that [ or test would be confused by. Putting an arbitrary non-metacharacter at the beginning eliminates that possibility. Commented Dec 9, 2011 at 19:15
7

the entire if-then and -z are unnecessary.

 [ "$foo" ] && echo "foo is not empty" [ "$foo" ] || echo "foo is indeed empty" 
2
  • 5
    That will fail if foo contains only spaces Commented May 20, 2010 at 21:16
  • 4
    Will also fail in some shells if foo begins with a dash since it is interpreted as an option. E.g. on solaris ksh, zsh and bash are no problem, sh and /bin/test will fail Commented Oct 20, 2011 at 7:40
6

The question asks how to check if a variable is an empty string and the best answers are already given for that.
But I landed here after a period passed programming in php and what I was actually searching was a check like the empty function in php working in a bash shell.
After reading the answers I realized I was not thinking properly in bash, but anyhow in that moment a function like empty in php would have been soooo handy in my bash code.
As I think this can happen to others, I decided to convert the php empty function in bash

According to the php manual:
a variable is considered empty if it doesn't exist or if its value is one of the following:

  • "" (an empty string)
  • 0 (0 as an integer)
  • 0.0 (0 as a float)
  • "0" (0 as a string)
  • an empty array
  • a variable declared, but without a value

Of course the null and false cases cannot be converted in bash, so they are omitted.

function empty { local var="$1" # Return true if: # 1. var is a null string ("" as empty string) # 2. a non set variable is passed # 3. a declared variable or array but without a value is passed # 4. an empty array is passed if test -z "$var" then [[ $( echo "1" ) ]] return # Return true if var is zero (0 as an integer or "0" as a string) elif [ "$var" == 0 2> /dev/null ] then [[ $( echo "1" ) ]] return # Return true if var is 0.0 (0 as a float) elif [ "$var" == 0.0 2> /dev/null ] then [[ $( echo "1" ) ]] return fi [[ $( echo "" ) ]] } 



Example of usage:

if empty "${var}" then echo "empty" else echo "not empty" fi 



Demo:
the following snippet:

#!/bin/bash vars=( "" 0 0.0 "0" 1 "string" " " ) for (( i=0; i<${#vars[@]}; i++ )) do var="${vars[$i]}" if empty "${var}" then what="empty" else what="not empty" fi echo "VAR \"$var\" is $what" done exit 

outputs:

VAR "" is empty VAR "0" is empty VAR "0.0" is empty VAR "0" is empty VAR "1" is not empty VAR "string" is not empty VAR " " is not empty 

Having said that in a bash logic the checks on zero in this function can cause side problems imho, anyone using this function should evaluate this risk and maybe decide to cut those checks off leaving only the first one.

1
  • 1
    Inside the function empty - why did you write [[ $( echo "1" ) ]] ; return instead of simply return 1 ? Commented Sep 28, 2016 at 19:33
5

Personally prefer more clear way to check :

if [ "${VARIABLE}" == "" ]; then echo VARIABLE is empty else echo VARIABLE is not empty fi 
5
  • 1
    Some shells do not accept the double equal sign. Commented Feb 25, 2012 at 12:19
  • 1
    The question was about bash. I'm using bash. Works well for me. What exactly You are speaking about ? Commented Feb 25, 2012 at 13:03
  • 3
    If you're using Bash, you should use double square brackets. My previous comment was a simple statement of fact for those who might read your answer and be using a different shell. Commented Feb 25, 2012 at 15:49
  • single square brackets are ok in this case, please see serverfault.com/questions/52034/… Commented Aug 31, 2012 at 19:21
  • @DennisWilliamson I agree that in bash you should always use [[...]]. So you can use if [[ $VARIABLE = "" ]] or if [[ $VARIABLE == "" ]], if you prefer to use == (I don't). You should be aware that this is actually a quoted string comparison within a pattern match, but it is so much more readable than [[ -z $VARIABLE ]]. Commented Jan 3 at 20:03
5

My 5 cents: there is also a shorter syntax than if ..., this one:

VALUE="${1?"Usage: $0 value"}" 

This line will set VALUE if an argument has been supplied and will print an error message prepended with the script line number in case of an error (and will terminate the script execution).

Another example can be found in the abs-guide (search for «Example 10-7»).

4

This is true exactly when $FOO is set and empty:

[ "${FOO+x}" = x ] && [ -z "$FOO" ] 
4

oneliner extension of duffbeer703's solution:

#! /bin/bash [ -z "$1" ] || some_command_that_needs_$1_parameter 
0

To figure out if a variable "Foo" is empty and also contains no spaces (or no whitespace as some people refer it).

 if [[ -n "${Foo/[ ]*\n/}" ]];then echo "Foo is not empty and contains non space characters" fi # Another way to solve the same problem: Take spaces out in Foo & check if Foo is empty if [[ -z "${Foo// }" ]];then echo "Foo is empty" fi 
0

I had to use the double square brackets approach as well. For example:

if [[ -z "${varname}" ]]; then echo "Missing varname."; exit 1; else echo "varname=\"${reponame}\""; fi 
-1

Not an exact answer, but ran into this trick. If the string you're looking for comes from "a command" then you can actually store the command in an env. variable and then execute it every time for the if statement, then no brackets required!

For example this command, which determines if you're on debian:

grep debian /proc/version

full example:

IS_DEBIAN="grep -i debian /proc/version" if $IS_DEBIAN; then echo 'yes debian' else echo 'non debian' fi 

So this is like an indirect way (rerunning it every time) to check for an empty string (it happens to be checking for error response from the command, but it also happens to be returning an empty string).

-1

I prefer the way how PHP checks for an empty variable as Luca mentioned in his answer, too. But instead of using a separate function, I use a "filter" which finally allows using usual bash conditions.

Imagine you have a bash script with a settings section like this:

# enable test mode dry_run=false # overwrite files if they already exist overwrite_files=true 

The users of the script should be able to set it as they like:

dry_run= dry_run="" dry_run=0 dry_run="0" dry_run=false dry_run="false" # or delete it completely 

So in the script section itself I do this:

# check user settings [[ $dry_run == 0 ]] || [[ $dry_run == false ]] && unset dry_run [[ $overwrite_files == 0 ]] || [[ $overwrite_files == false ]] && unset overwrite_files 

Now, I can be sure that the variable exists with a content or not, so I can use a usual condition like this:

if [[ $dry_run ]]; then echo "dry run is true" else echo "dry run is false" fi 

PS I skipped filtering "0.0" as I don't think a user would disable a setting by setting this, but feel free to extend the filter if you need it.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.