Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

The following bash syntax verifies if param isn't empty:

 [[ !  -z  $param  ]]

For example:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

No output and its fine.

But when param is empty except for one (or more) space characters, then the case is different:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

"I am not zero" is output.

How can I change the test to consider variables that contain only space characters as empty?

share|improve this question
1  
From man test: -z STRING - the length of STRING is zero. If you want to remove all spaces in $param, use ${param// /} –  dchirikov Jul 28 at 9:27

6 Answers 6

up vote 14 down vote accepted

First, note that the -z test is explicitly for:

the length of string is zero

That is, a string containing only spaces should not be true under -z, because it has a non-zero length.

What you want is to remove the spaces from the variable using the pattern replacement parameter expansion:

[[ -z "${param// }" ]

This expands the param variable and replaces all matches of the pattern (a single space) are deleted, so a string that has only spaces in it will be expanded to an empty string.


The nitty-gritty of how that works is that ${var/pattern/string} replaces the first longest match of pattern with string. When pattern starts with / (as above) then it replaces all the matches. Because the replacement is empty, we can omit the final / and the string value:

${parameter/pattern/string}

The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. If pattern begins with ‘/’, all matches of pattern are replaced with string. Normally only the first match is replaced. ... If string is null, matches of pattern are deleted and the / following pattern may be omitted.

After all that, we end up with ${param// } to delete all spaces.

Note that though present in ksh (where it originated), zsh and bash, that syntax is not POSIX and should not be used in sh scripts.

share|improve this answer
1  
That is true. The question title has been edited in a way that changes its meaning on that technical level. You can of course test whether the variable is set first with ![ -n "${param+set}" ]. It's not relevant to the asker's own issue though. –  Michael Homer Jul 29 at 2:50
    
My apologies - I was unaware of the edit. And you're correct - the edit fundamentally changes the meaning of the question. I will say, though, that whether or not a variable is set should be considered relevant to any variable test. –  mikeserv Jul 29 at 3:03

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

To differentiate between blank, non-blank, empty, unset:

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac
share|improve this answer

The easy way to check that a string only contains characters in an authorized set is to test for the presence of unauthorized characters. Thus, instead of testing whether the string only contains spaces, test whether the string contains some character other than space. In bash, ksh or zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

“Consists of spaces only” includes the case of an empty (or unset) variable.

You may want to test for any whitespace character. Use [[ $param = *[^[:space:]]* ]] to use locale settings, or whatever explicit list of whitespace characters you want to test for, e.g. [[ $param = *[$' \t\n']* ]] to test for space, tab or newline.

Matching a string against a pattern with = inside [[ … ]] is a ksh extension (also present in bash and zsh). In any Bourne/POSIX-style, you can use the case construct to match a string against a pattern. Note that standard shell patterns use ! to negate a character set, rather than ^ like in most regular expression syntaxes.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

To test for whitespace characters, the $'…' syntax is specific to ksh/bash/zsh; you can insert these characters in your script literally (note that a newline will have to be within quotes, as backslash+newline expands to nothing), or generate them, e.g.

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac
share|improve this answer
    
This is almost excellent - but, as yet, it does not answer the question as asked. It currently can tell you the difference between an unset or empty shell variable and one which contains only spaces. If you will accept my suggestion you will add the param expansion form not yet convered by any other answer that explicitly tests a shell variable for being set - I mean ${var?not set} - passing that test and surviving would ensure that a variable matched by your *) case would effect a definitive answer, for example. –  mikeserv Jul 29 at 2:06
    
Correction - this, in fact, would answer the question originally asked, but it has since been edited in such a way that it fundamentally changes the meaning of the question and therefore invalidates your answer. –  mikeserv Jul 29 at 4:09
    
You need [[ $param = *[!\ ]* ]] in mksh. –  Stéphane Chazelas Jul 30 at 6:01

The only remaining reason to write a shell script, instead of a script in a good scripting language, is if extreme portability is an overriding concern. The legacy /bin/sh is the only thing you can be certain you have, but Perl for instance is more likely to be available cross-platform than Bash. Therefore, never write shell scripts that use features that aren't truly universal -- and keep in mind that several proprietary Unix vendors froze their shell environment prior to POSIX.1-2001.

There is a portable way to make this test, but you have to use tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(The default value of $IFS, conveniently, is a space, a tab, and a newline.)

(The printf builtin is itself spottily portable, but relying on it is much less hassle than figuring out which variant of echo you have.)

share|improve this answer
1  
That's a bit convoluted. The usual portable way to test whether a string contains certain characters is with case. –  Gilles Jul 29 at 0:38
    
@Gilles I don't think it's possible to write a case glob to detect a string which is either empty, or contains only whitespace characters. (It would be easy with a regexp, but case doesn't do regexps. The construct in your answer won't work if you care about newlines.) –  Zack Jul 29 at 1:27
1  
I gave spaces as an example in my answer because that's what the question asked for. It's equally easy to test for whitespace characters: case $param in *[![:space:]]*) …. If you want to test for IFS characters, you can use the pattern *[$IFS]*. –  Gilles Jul 29 at 1:44
1  
@mikeserv In a really seriously defensive script, you explicitly set IFS to <space><tab><newline> at the beginning and then never touch it again. I thought that'd be a distraction. –  Zack Jul 29 at 14:00
1  
Regarding variables in patterns, I think that works in all Bourne versions and all POSIX shells; it would require extra code to detect case patterns and turn off variable expansion and I can't think of a reason to do it. I have a couple of instances of it in my .profile which has been executed by many Bourne shells. Of course [$IFS] won't do what was intended if IFS has certain non-default values (empty (or unset), containing ], starting with ! or ^). –  Gilles Jul 29 at 16:26

In order to determine if a variable is empty you first determine if it is set. An unset variable and an empty variable are not one and the same. This can be especially useful information before trying to set a variable that has been set as empty (sometimes used to represent a condition is true for environment variables) and readonly. If you attempt to do this in a script your script will very likely die.

(
    readonly var=
    [ -z "$var" ] && var=something
    echo The subshell is dead by now.
)
sh: line 3: var: readonly variable

Thankfully, this is easily tested.

The parameter expansion form ${var+WORD} will expand $var to WORD so long as it is set at all, and the form ${var:+WORD} expands to WORD only in the event it is set and its value is non-null. The reverse of these forms substitues a -dash in the + operator's place, and expand to WORD when ${var- is unset or when ${var:- is unset or empty.

Combining these forms can make for very simple tests.

(
    readonly var=
    ${var+:} false || var=something
    echo "${var:-But this subshell survives!}"
)
But this subshell survives!

A variable's length is represented in the parameter expansion form ${#var}. This evaluates to zero for unset variables and for empty variables. Characters can be portably stripped from a variable using shell globs and the ${var#PATTERN} or ${var%PATTERN} forms. All of this together makes it a simple matter to handle testing whether a variable is set, empty and/or contains only spaces like:

i= ; set -- '    ' 'st  ri
ng' '' 
while i=$((i+1)) && [ -n "${1+?}" ] &&
    printf " \$$i is set and %s\n %s" \
        "${1:+not }empty." \   
        "${1:+\$$i contains }"
do  [ -n "$1" ] && ( l=${#1} IFS=' '
    set -f; set -- $1 ; IFS= ; s="$*"
    echo "$((l-${#s})) spaces and ${#s} other chars."
) ; shift ; done
echo "\$$i is not set!"

OUTPUT

 $1 is set and not empty.
 $1 contains 4 spaces and 0 other chars.
 $2 is set and not empty.
 $2 contains 3 spaces and 7 other chars.
 $3 is set and empty.
 $4 is not set!
share|improve this answer

For testing if variable is empty or contain spaces, you can also use this code:

${name:?variable is empty}
share|improve this answer
    
I think your answer is downvoted because the "or contain spaces" is not true. –  Bernhard Jul 29 at 11:19
    
be careful - that kills the current shell. Better to put it in a (: ${subshell?}). Also - that will write to stderr - you probably want redirect. And most important that evaluates to the variable's actual value if it is not unset or null. If there's a command in there, you just ran it. It's best to run that test on :. –  mikeserv Jul 29 at 12:59
    
how it will kill shell. and u can also test this , first define name=aaaa then run this command echo ${name:?variable is empty} it will print value of variable name and now run this command echo ${name1:?variable is empty} it will print -sh: name1: variable is empty –  pratik Jul 29 at 16:24
    
Yes - echo ${name:?variable is empty} will either evaluate to $name's non-null value or it will kill the shell. So for the same reason you don't do prompt > $randomvar neither should you ${var?} - if there's a command in there, it's probably going to run - and you don't know what it is. An interactive shell doesn't have to exit - which is why yours doesn't. Still, if you want to see some tests, there are a lot of them here.. This one isn't quite as long... –  mikeserv Jul 30 at 2:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.