If you've worked with Bash for any length of time, you've probably encountered both [[ ... ]]
and [ ... ]
(or test) for writing conditional expressions. At first glance, they may seem interchangeable. But subtle differences between the two can lead to surprising behavior — especially when handling empty strings.
Let’s explore one such "gotcha" scenario.
The Confusing Behavior
Consider this simple Bash script:
Note: man page of test
-n STRING: the length of STRING is nonzero
-z STRING: the length of STRING is zero
VAR_STR=""
if [[ -n $VAR_STR ]]; then
echo "var str is nonzero with [["
else
echo "var str is zero with [["
fi
if [ -n $VAR_STR ]; then
echo "var str is nonzero with ["
else
echo "var str is zero with ["
fi
When executed, the output is:
var str is zero with [[
var str is nonzero with [
Wait, what? Why is the behavior different? Why does [[ -n $VAR_STR ]]
correctly detect the empty string, but [ -n $VAR_STR ]
behaves as though the string is non-empty?
Let’s break it down.
What’s Happening Here?
[[ ... ]]
in Bash:[[ ... ]]
is a Bash keyword designed for more robust and predictable conditional expressions. When you use[[ ... ]].
Bash treats the entire expression as a syntactic construct. It handles variables safely, even when they are unquoted or empty.-n $VAR_STR
checksif $VAR_STR
is non-empty, and since$VAR_STR
is an empty string, the condition evaluates to false.[ ... ]
in Bash (POSIX Test):[ ... ]
is a POSIX-compliant command (an alias for test) that behaves like a standard utility. When you use[ ... ].
Bash expands the condition and passes the resulting arguments to the test command. If$VAR_STR
is empty and unquoted, the resulting expression becomes[ -n ].
Here, -n is treated as a literal non-empty string, so the condition evaluates to true.
The Root Cause: Unquoted Variables
In the [ ... ]
test, failing to quote $VAR_STR
causes it to expand to nothing
( [ -n ]
), which leads to unexpected behavior. This is why quoting variables is essential when using [ ... ]
.
How to Fix This
To make your code work consistently, always quote variables when using [ ... ]
. Here’s the corrected script:
VAR_STR=""
if [[ -n $VAR_STR ]]; then
echo "var str is nonzero with [["
else
echo "var str is zero with [["
fi
if [ -n "$VAR_STR" ]; then
echo "var str is nonzero with ["
else
echo "var str is zero with ["
fi
Output:
var str is zero with [[
var str is zero with [
Now both [[ ... ]]
and [ ... ]
behave as expected.
Best Practices
Prefer
[[ ... ]]
in Bash ScriptsIt’s safer and easier to use for most scenarios.
Quoting variables is optional but still good practice.
Always Quote Variables in
[ ... ]
Quoting ensures your script behaves predictably, even with empty or special characters.
Know Your Shell: Understand whether you’re targeting Bash or a generic POSIX shell (/bin/sh). Use the appropriate construct accordingly.
In modern Bash scripting, [[ ... ]]
is almost always the better choice. But if you need to use [ ... ]
: always quote your variables!
Conclusion
Bash has many such gotchas due it’s subtle nature. These gotchas can be significantly time consuming to debug if you don’t know them. Have you encountered other "gotchas" in Bash scripting?
Share them with us—we’d love to discuss more!