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?
12 Answers 12
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) with nothing, 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.
-
4use
sed
they said... justecho
the variable they said...mikezter– mikezter2017年05月15日 23:57:02 +00:00Commented May 15, 2017 at 23:57 -
11Hmm. If it is
${var/pattern/string}
then shouldn't it be[[ -z ${param/ /} ]]
with the space between the slashes to be the pattern and nothing after to be the string?Jesse Chisholm– Jesse Chisholm2017年05月22日 17:51:53 +00:00Commented May 22, 2017 at 17:51 -
@JesseChisholm "Because the replacement is empty, we can omit the final / and the string value"; "If string is null, matches of pattern are deleted and the / following pattern may be omitted."Michael Homer– Michael Homer2017年05月22日 19:21:03 +00:00Commented May 22, 2017 at 19:21
-
11@MichaelHomer The answer
[[ -z "${param// }" ]]
sure looks like thepattern
is empty and thereplacement string
is a single space. AH! I see it nowif pattern begins with a slash
I missed that part. so the pattern is/
and the string is left off and therefore empty. Thanks.Jesse Chisholm– Jesse Chisholm2017年06月09日 00:59:59 +00:00Commented Jun 9, 2017 at 0:59 -
bash note: if running in set -o nounset (set -u), and param is unset (rather than null or space-filled), then this will generate an unbound variable error. ( set +u; ..... ) would exempt this test.Brian Chrisman– Brian Chrisman2017年12月08日 07:11:46 +00:00Commented Dec 8, 2017 at 7:11
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
-
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– mikeserv2014年07月29日 02:06:23 +00:00Commented Jul 29, 2014 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– mikeserv2014年07月29日 04:09:50 +00:00Commented Jul 29, 2014 at 4:09
-
You need
[[ $param = *[!\ ]* ]]
inmksh
.Stéphane Chazelas– Stéphane Chazelas2014年07月30日 06:01:29 +00:00Commented Jul 30, 2014 at 6:01
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
[:blank:]
is for horizontal spacing characters (space and tab in ASCII, but there are probably a few more in your locale; some systems will include the non-breaking space (where available), some won't). If you want vertical spacing characters as well (like newline or form-feed), replace [:blank:]
with [:space:]
.
-
POSIXly is ideal because it will work with the (arguably better) dash.Ken Sharp– Ken Sharp2015年12月04日 13:52:11 +00:00Commented Dec 4, 2015 at 13:52
-
zsh
seems to have problem with[![:blank:]]
, it raise:b
is invalid modifier. Insh
andksh
emulate, it raise event not found for[
cuonglm– cuonglm2016年01月21日 16:03:33 +00:00Commented Jan 21, 2016 at 16:03 -
@cuonglm, what did you try?
var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'
is OK for me. The event not found would only be for interactive shells.Stéphane Chazelas– Stéphane Chazelas2016年01月21日 16:27:53 +00:00Commented Jan 21, 2016 at 16:27 -
@StéphaneChazelas: Ah, right, I tried with interactive shells. The
:b
invalid modifier is a bit strange.cuonglm– cuonglm2016年01月21日 16:53:42 +00:00Commented Jan 21, 2016 at 16:53 -
1@FranklinYu, on OS/X
sh
isbash
built with--enable-xpg-echo-default
and--enable-strict-posix-default
so as to be Unix compliant (which involvesecho '\t'
outputting a tab).Stéphane Chazelas– Stéphane Chazelas2016年08月09日 20:20:03 +00:00Commented Aug 9, 2016 at 20:20
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.)
-
1That's a bit convoluted. The usual portable way to test whether a string contains certain characters is with
case
.Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2014年07月29日 00:38:42 +00:00Commented Jul 29, 2014 at 0:38 -
1I 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 forIFS
characters, you can use the pattern*[$IFS]*
.Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2014年07月29日 01:44:09 +00:00Commented Jul 29, 2014 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.zwol– zwol2014年07月29日 14:00:08 +00:00Commented Jul 29, 2014 at 14:00 -
1Regarding 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 ifIFS
has certain non-default values (empty (or unset), containing]
, starting with!
or^
).Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2014年07月29日 16:26:04 +00:00Commented Jul 29, 2014 at 16:26 -
1Regarding the reason behind using shell script, it is the best scripting language to interact with other system tools and scripting actual terminal-based workflow. You can't just throw the language out the window and criticize everyone who uses it just because it doesn't apply to you.Jean-Luc Nacif Coelho– Jean-Luc Nacif Coelho2017年10月30日 23:23:11 +00:00Commented Oct 30, 2017 at 23:23
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
#execute if the the variable is not empty and contains non space characters
else
#execute if the variable is empty or contains only spaces
fi
-
this gets rid of any white space character, not just a single space, right? thanksAlexander Mills– Alexander Mills2016年12月11日 01:02:48 +00:00Commented Dec 11, 2016 at 1:02
For testing if variable is empty or contain spaces, you can also use this code:
${name:?variable is empty}
-
3I think your answer is downvoted because the "or contain spaces" is not true.Bernhard– Bernhard2014年07月29日 11:19:33 +00:00Commented Jul 29, 2014 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– mikeserv2014年07月29日 12:59:32 +00:00Commented Jul 29, 2014 at 12:59 -
how it will kill shell. and u can also test this , first define
name=aaaa
then run this commandecho ${name:?variable is empty}
it will print value of variable name and now run this commandecho ${name1:?variable is empty}
it will print -sh: name1: variable is emptypratik– pratik2014年07月29日 16:24:15 +00:00Commented Jul 29, 2014 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 doprompt > $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– mikeserv2014年07月30日 02:31:30 +00:00Commented Jul 30, 2014 at 2:31
The code you posted [[ ! -z $param ]] && echo "I am not zero"
will print I am not zero
if param is either unset/undefined or empty (in bash, ksh and zsh).
To also print if param contains only spaces (remove spaces), POSIXly (for a wider range of shells):
[ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"
Explanation:
- A
${param# ... }
removes a leading text. - That leading text is given by:
${param%%[! ]*}
- Which expands to all spaces before any non-space character, i.e. all the leading spaces.
The end result of the whole expansion is that all leading spaces get removed.
This expanded result is tested if of length 0.
- If the result is empty, then either the variable was empty, or
- removing the leading spaces made it empty.
Therefore, if the test is true, the variable was either empty already or only had spaces inside it.
The concept is easy to extend to more character types. To also include tabs, place an explicit tab inside the bracket expression:
[ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"
or use a variable with the characters that you need removed:
var=$' \t' # in ksh, bash, zsh
[ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"
or you can use some POSIX "character class expression" (blank means space and tab):
[ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"
To check if variable has only spaces, including in a multi-line variable, try this:
[[ $var = *[$" \t\n"]* ]]
Or with xargs:
[[ -z $(echo $var | xargs) ]]
Or with sed:
[[ -z $(sed "/^$/d" <<< $var) ]]
-
There are a lot of non-blank values of
$var
for which[[ -z $(echo $var | xargs) ]]
would return true, like-n
,-e \x20
,"" '' \ \ \ ""
, etc, not to mention all the issues related to globbing.Stéphane Chazelas– Stéphane Chazelas2022年01月11日 12:48:12 +00:00Commented Jan 11, 2022 at 12:48 -
You're confusing
$"..."
with$'...'
Stéphane Chazelas– Stéphane Chazelas2022年01月11日 12:48:58 +00:00Commented Jan 11, 2022 at 12:48
I prefer the solution without trimming
BLANK_STRING_REGEX='^ *$'
TEXT=" "
if [[ $TEXT =~ $BLANK_STRING_REGEX ]] ; then
echo "Blank string"
fi
I've also noticed that it's often useful to check whether a string contains spaces and new line chars only:
BLANK_STRING_REGEX='^\s*$'
TEXT="
"
if [[ $TEXT =~ $BLANK_STRING_REGEX ]] ; then
echo "Blank string"
fi
-
Note that bash uses the system's regexp API to do regexp matching.
\s
is not a standard regexp operator, so that won't work on all systems. The standard equivalent is[[:space:]]
(and[[:blank:]]
for the\h
equivalent).Stéphane Chazelas– Stéphane Chazelas2022年01月11日 13:08:33 +00:00Commented Jan 11, 2022 at 13:08
echo ${param//*[^[:space:]]*/I am not zero}
I prefer this:
[[ $(echo "$param" | awk '{print NF}') -eq 0 ]]
-
awk would also consider tabs as "empty", not just spaces. Also see: When is double-quoting necessary?ilkkachu– ilkkachu2022年01月11日 12:08:25 +00:00Commented Jan 11, 2022 at 12:08
-
There are also potential problems with using
echo
on arbitrary data.2022年01月11日 12:41:14 +00:00Commented Jan 11, 2022 at 12:41 -
That would also not work properly for variables containing newline characters.Stéphane Chazelas– Stéphane Chazelas2022年01月11日 12:50:51 +00:00Commented Jan 11, 2022 at 12:50
Try this:
$ [[ -z \`echo $n\` ]] && echo zero
zero
man test
:-z STRING - the length of STRING is zero
. If you want to remove all spaces in$param
, use${param// /}
trim()
function built-in to any *nix at all? So many different hacks to achieve something so simple...[
is equivalent totest
. Sometimes it used to be a symlink. But[[
is BASH specific and there are many differences to programtest
. The reason of creating [[ was to make it easier to construct conditions. For that purpose it can't be the same as '[' ortest
.