[ ] vs [[ ]] in Bash: Single vs Double Brackets

Understand the difference between [ ] (test) and [[ ]] in bash. Learn when to use each, POSIX compatibility, and common mistakes with == vs =.

Deprecated Syntax

Detailed Explanation

Single Brackets [ ] vs Double Brackets [[ ]]

The choice between [ ] and [[ ]] is one of the most confusing aspects of shell scripting. They look similar but behave quite differently.

[ ] — The test Command

[ ] is actually the test command. It is a regular command, not shell syntax:

# These are equivalent:
[ "$var" = "hello" ]
test "$var" = "hello"

Rules for [ ]:

  • Variables MUST be quoted: [ "$var" = "value" ]
  • Use = for string comparison (not ==)
  • Use -eq, -lt, -gt for numeric comparison
  • No pattern matching or regex
  • && and || must be outside: [ cond1 ] && [ cond2 ]

[[ ]] — Bash Keyword

[[ ]] is a Bash/Zsh/Ksh built-in keyword with additional features:

# No word splitting — quoting optional (but still recommended)
[[ $var = "hello" ]]

# Pattern matching with ==
[[ $file == *.txt ]]

# Regex matching with =~
[[ $email =~ ^[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]+$ ]]

# Logical operators inside
[[ $a -gt 0 && $b -lt 100 ]]

Common Mistake: == in [ ]

# WRONG: == is not POSIX in [ ]
if [ "$var" == "value" ]; then  # Works in bash but not sh

# CORRECT for [ ]:
if [ "$var" = "value" ]; then

# CORRECT for [[ ]]:
if [[ "$var" == "value" ]]; then

Comparison Table

Feature [ ] [[ ]]
POSIX compatible Yes No (bash/zsh/ksh)
Word splitting Yes (must quote) No
Pattern matching No == *.txt
Regex No =~ regex
Logical operators External &&/`
String comparison = = or ==

Recommendation

  • Use [[ ]] in Bash scripts (with #!/bin/bash shebang)
  • Use [ ] only when POSIX compatibility is required (#!/bin/sh)
  • Always quote variables in [ ] to prevent word splitting

Use Case

Writing conditional logic in shell scripts, validating user input, checking file existence and types, and pattern matching on strings. Understanding the difference is essential for anyone writing shell scripts that need to be portable or that use advanced matching.

Try It — Shell Script Linter

Open full tool