In bash scripting, both [
(single bracket) and [[
(double bracket) are used for conditional expressions, but they differ fundamentally in implementation and capabilities:
# Traditional test command (POSIX)
[ "$STRING" != "foo" ]
# Bash extended test construct
[[ $STRING != foo ]]
- Parsing Behavior:
[
is a builtin that behaves like a command (originally linked to/bin/[
), while[[
is a bash keyword with special parsing rules - Word Splitting:
[
requires quotes to prevent word splitting, whereas[[
handles variables more intelligently - Pattern Matching:
[[
supports==
with glob patterns and=~
for regex
Let's measure the execution time difference:
time for i in {1..10000}; do [ "$STRING" != "foo" ]; done
time for i in {1..10000}; do [[ $STRING != foo ]]; done
Typical results show [[
is 2-3x faster due to being a shell builtin rather than an external command.
Where [[
really shines:
# Regex matching
if [[ $filename =~ ^[A-Za-z0-9_]+\.(txt|log)$ ]]; then
echo "Valid filename"
fi
# Compound expressions
if [[ -d "$dir" && -w "$dir" ]]; then
echo "Writable directory"
fi
# No quotes needed for right-hand glob
if [[ $file == *.config ]]; then
echo "Config file detected"
fi
Single [ |
Double [[ |
---|---|
POSIX-compliant scripts | Bash-specific scripts |
System init scripts | Interactive shell scripts |
Simple comparisons | Complex pattern matching |
Watch out for these mistakes:
# Wrong: Missing quotes in [ ]
[ $var = "value" ] # Fails if $var contains spaces
# Wrong: Using -a/-o in [[ ]]
[[ $a -eq 1 -a $b -eq 2 ]] # Should use && instead
# Wrong: Using =~ with quoted pattern
[[ $string =~ "pattern" ]] # Quotes make it literal
In Bash scripting, both [ ]
(single brackets) and [[ ]]
(double brackets) are used for conditional testing, but they have significant differences in functionality and behavior.
The primary distinctions between these two constructs include:
[ ]
is a built-in command (actually an alias fortest
)[[ ]]
is a Bash keyword with enhanced features[[ ]]
is generally safer and more powerful
Let's examine how each handles string comparison:
STRING="some value"
# Using single brackets
if [ "$STRING" != "foo" ]; then
echo "Not equal (single brackets)"
fi
# Using double brackets
if [[ $STRING != foo ]]; then
echo "Not equal (double brackets)"
fi
[[ ]]
offers several improvements:
- No need for quoting variables in most cases
- Supports pattern matching with
==
and=~
- Has logical operators
&&
and||
instead of-a
and-o
- Better handling of empty variables
Here's how pattern matching differs:
filename="document.txt"
# This won't work properly with single brackets
if [[ $filename == *.txt ]]; then
echo "Text file detected"
fi
# Equivalent with single brackets requires extra quoting
if [ "$filename" = "*.txt" ]; then
echo "This won't work as expected"
fi
Recommendations for usage:
- Use
[[ ]]
for Bash scripts (when portability isn't required) - Use
[ ]
for POSIX-compliant scripts - Always prefer
[[ ]]
for complex conditions
Double brackets are generally faster as they don't require:
- Word splitting
- Filename expansion
- External command invocation
Watch out for these issues:
# Problematic with single brackets
if [ $undefined_var = "value" ]; then
echo "This may fail"
fi
# Safer with double brackets
if [[ $undefined_var = "value" ]]; then
echo "This handles undefined variables"
fi
Double brackets support additional operators:
# Regular expression matching
if [[ "hello" =~ ^h ]]; then
echo "Starts with h"
fi
# Numeric comparison without -lt/-gt
if (( 10 > 5 )); then
echo "10 is greater than 5"
fi