I've seen Bash scripts test for a non-zero length string in two different ways. Most scripts use the -n
option:
#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
# Do something when var is non-zero length
fi
But the -n option isn't really needed:
# Without the -n option
if [ "$var" ]; then
# Do something when var is non-zero length
fi
Which is the better way?
Similarly, which is the better way for testing for zero-length:
if [ -z "$var" ]; then
# Do something when var is zero-length
fi
or
if [ ! "$var" ]; then
# Do something when var is zero-length
fi
Edit: This is a more complete version that shows more differences between [
(aka test
) and [[
.
The following table shows that whether a variable is quoted or not, whether you use single or double brackets and whether the variable contains only a space are the things that affect whether using a test with or without -n/-z
is suitable for checking a variable.
| 1a 2a 3a 4a 5a 6a | 1b 2b 3b 4b 5b 6b
| [ [" [-n [-n" [-z [-z" | [[ [[" [[-n [[-n" [[-z [[-z"
-----+------------------------------------+------------------------------------
unset| false false true false true true | false false false false true true
null | false false true false true true | false false false false true true
space| false true true true true false| true true true true false false
zero | true true true true false false| true true true true false false
digit| true true true true false false| true true true true false false
char | true true true true false false| true true true true false false
hyphn| true true true true false false| true true true true false false
two | -err- true -err- true -err- false| true true true true false false
part | -err- true -err- true -err- false| true true true true false false
Tstr | true true -err- true -err- false| true true true true false false
Fsym | false true -err- true -err- false| true true true true false false
T= | true true -err- true -err- false| true true true true false false
F= | false true -err- true -err- false| true true true true false false
T!= | true true -err- true -err- false| true true true true false false
F!= | false true -err- true -err- false| true true true true false false
Teq | true true -err- true -err- false| true true true true false false
Feq | false true -err- true -err- false| true true true true false false
Tne | true true -err- true -err- false| true true true true false false
Fne | false true -err- true -err- false| true true true true false false
If you want to know if a variable is non-zero length, do any of the following:
-n
and quote the variable in single brackets (column 4a)-n
(columns 1b - 4b)Notice in column 1a starting at the row labeled "two" that the result indicates that [
is evaluating the contents of the variable as if they were part of the conditional expression (the result matches the assertion implied by the "T" or "F" in the description column). When [[
is used (column 1b), the variable content is seen as a string and not evaluated.
The errors in columns 3a and 5a are caused by the fact that the variable value includes a space and the variable is unquoted. Again, as shown in columns 3b and 5b, [[
evaluates the variable's contents as a string.
Correspondingly, for tests for zero-length strings, columns 6a, 5b and 6b show the correct ways to do that. Also note that any of these tests can be negated if negating shows a clearer intent than using the opposite operation. For example: if ! [[ -n $var ]]
.
If you're using [
, the key to making sure that you don't get unexpected results is quoting the variable. Using [[
, it doesn't matter.
The error messages, which are being suppressed, are "unary operator expected" or "binary operator expected".
This is the script that produced the table above.
#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal
dw=5 # description column width
w=6 # table column width
t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }
o=/dev/null
echo ' | 1a 2a 3a 4a 5a 6a | 1b 2b 3b 4b 5b 6b'
echo ' | [ [" [-n [-n" [-z [-z" | [[ [[" [[-n [[-n" [[-z [[-z"'
echo '-----+------------------------------------+------------------------------------'
while read -r d t
do
printf '%-*s|' "$dw" "$d"
case $d in
unset) unset t ;;
space) t=' ' ;;
esac
[ $t ] 2>$o && t || f
[ "$t" ] && t || f
[ -n $t ] 2>$o && t || f
[ -n "$t" ] && t || f
[ -z $t ] 2>$o && t || f
[ -z "$t" ] && t || f
echo -n "|"
[[ $t ]] && t || f
[[ "$t" ]] && t || f
[[ -n $t ]] && t || f
[[ -n "$t" ]] && t || f
[[ -z $t ]] && t || f
[[ -z "$t" ]] && t || f
echo
done <<'EOF'
unset
null
space
zero 0
digit 1
char c
hyphn -z
two a b
part a -a
Tstr -n a
Fsym -h .
T= 1 = 1
F= 1 = 2
T!= 1 != 2
F!= 1 != 1
Teq 1 -eq 1
Feq 1 -eq 2
Tne 1 -ne 2
Fne 1 -ne 1
EOF