Introduction
Recently, I had an opportunity to write some shell scripts at work.
Since I didn’t have much experience with shell scripting, I looked things up online while writing the code. That’s when I ran into this question:
What’s the difference between writing
if [ ]; thenandif [[ ]]; then?
In this article, I’ll share what I learned about the differences between the two and how to choose which one to use.
The Basic Difference Between [ ] and [[ ]]
| Syntax | Type | Characteristics |
|---|---|---|
if [ condition ]; then | POSIX-standard | Works on any UNIX environment. The classic format. |
if [[ condition ]]; then | Bash extension | Bash-only. Safer and more powerful. |
The POSIX-standard syntax is defined so that it behaves the same across all UNIX-like systems.
It works on Linux, macOS, BSD, and any POSIX-compliant shell.
On the other hand, the Bash extension syntax works on Bash and Zsh, but does not work on /bin/sh or other strictly POSIX-compliant shells.
[ ] internally calls the test command.
So this:
if [ -f file ]; then
echo "Exists"
fi
is actually equivalent to:
if test -f file; then
echo "Exists"
fi
Meanwhile, [[ ]] is parsed directly by the shell, which allows safer handling of complex expressions and pattern matching.
Common Conditional Expressions Compared
Here are common examples written using both [ ] and [[ ]].
1. Checking File or Directory Existence
| Condition | Meaning | [ ] | [[ ]] |
|---|---|---|---|
| File exists | -f | [ -f "$file" ] | [[ -f $file ]] |
| Directory exists | -d | [ -d "$dir" ] | [[ -d $dir ]] |
| Does not exist | ! | [ ! -f "$file" ] | [[ ! -f $file ]] |
Both work the same, but with [[ ]] you can omit quotes safely.
2. String Comparison
| Condition | Meaning | [ ] | [[ ]] |
|---|---|---|---|
| Equal | = | [ "$a" = "$b" ] | [[ $a = $b ]] |
| Not equal | != | [ "$a" != "$b" ] | [[ $a != $b ]] |
| Empty | -z | [ -z "$a" ] | [[ -z $a ]] |
| Not empty | -n | [ -n "$a" ] | [[ -n $a ]] |
[ ]→ Always quote variables
[[ ]]→ Quotes optional and safer
3. Numeric Comparison
| Condition | Meaning | [ ] | [[ ]] |
|---|---|---|---|
| Equal | -eq | [ "$x" -eq "$y" ] | [[ $x -eq $y ]] |
| Greater | -gt | [ "$x" -gt "$y" ] | [[ $x -gt $y ]] |
| Smaller | -lt | [ "$x" -lt "$y" ] | [[ $x -lt $y ]] |
Same syntax in both cases.
4. AND / OR Conditions
[ ] syntax
[ -f "$a" ] && [ -r "$a" ] # AND(both true)
[ -f "$a" ] || [ -f "$b" ] # OR(one true)
[[ ]] syntax
[[ -f $a && -r $a ]] # AND(both true)
[[ -f $a || -f $b ]] # OR(one true)
[[ ]] allows combining conditions inside a single expression.
5. Pattern Matching
file="report.txt"
# Works in [[ ]]
if [[ $file == *.txt ]]; then
echo "Text file"
fi
# Does NOT work in [ ] (always false)
if [ $file == *.txt ]; then
echo "Will not run"
fi
Wildcards (*, ?) are supported only in [[ ]].
How to Choose
| Use Case | Recommended Syntax |
|---|---|
Needs POSIX compatibility (/bin/sh) | [ ] |
| Bash-only script, want safety | [[ ]] |
| Avoiding quote-related bugs | [[ ]] |
| Needs compatibility with other shells | [ ] |
Pick One Style and Stick to It
You can use either style, but it’s best to choose one and stay consistent.
Here’s why:
1. Better readability and maintainability
Mixing [ ] and [[ ]] in one script makes readers wonder:
“Why is this one using
[ ]? Is there a special reason?”
Unintended inconsistencies increase mental load and confusion.
2. Prevent incompatibility across shells
[[ ]] works only in Bash.
It fails in /bin/sh:
#!/bin/sh
if [[ -f file ]]; then
echo "Exists"
fi
- Works in bash
- Errors in sh
Therefore:
- Writing a Bash script? → Use
[[ ]]
- Want POSIX portability? → Use
[ ]
3. Avoid subtle bugs
Since [ ] and [[ ]] evaluate expressions differently, mixing them can cause unexpected behavior.
Example:
var="*.txt"
# [ ] → filename expansion occurs
if [ $var = "*.txt" ]; then
echo "match"
fi
# [[ ]] → treated as a pattern
if [[ $var == *.txt ]]; then
echo "match"
fi
Even though the lines look similar, their behavior differs.
Consistency prevents surprises.
Summary
[ ]is the POSIX-standard test command
[[ ]]is a Bash extension: safer and more flexible- Both support
-n/-z, but[[ ]]is safer without quotes
[[ ]]supports combining conditions and pattern matching- Either is fine—but don’t mix them
Closing Thoughts
Before writing this article, I didn’t even know there were two different syntaxes.
I was copying conditional expressions from the internet and ended up mixing [ ] and [[ ]] in the same script without realizing it.
If this article helps even one person avoid that mistake, I’ll be happy.
Be mindful when choosing which syntax to use!
💬 How about you?
- What syntax do you usually use in your shell scripts —
[ ]or[[ ]]? And why? - Have you ever run into unexpected behavior because of differences between
[ ]and[[ ]]?
SOCIAL SHARE CARD GENERATOR