Understanding test, [ ], [[ ]], and (( )) in Bash
When writing Bash scripts, you will often need to evaluate conditions. Bash provides several ways to do this:
-
test -
[ ... ] -
[[ ... ]] -
(( ... ))
Although they may look similar, they behave quite differently. Understanding the differences will help you write safer and more powerful Bash scripts.
1. Inspecting Bash Test Commands
Let’s start by checking how Bash treats these constructs internally.
help '[['
help '['
Now check how Bash resolves them:
[root@oel01db Shell-Scripting]# type -a test
test is a shell builtin
test is /usr/bin/test
[root@oel01db Shell-Scripting]# type -a '['
[ is a shell builtin
[ is /usr/bin/[
[root@oel01db Shell-Scripting]# type -a '[['
[[ is a shell keyword
[root@oel01db Shell-Scripting]#
Notice something important:
-
test→ shell builtin and external command -
[→ shell builtin and external command -
[[→ shell keyword only
This tells us:
| Command | Type |
|---|---|
test | builtin + external command |
[ ] | builtin + external command |
[[ ]] | Bash keyword |
2. test and [ ] Are Basically the Same
The [ command is essentially another way to write test.
Example:
[root@oel01db Shell-Scripting]# /usr/bin/[ -f /etc/passwd ]
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# echo $?
0
[root@oel01db Shell-Scripting]#
Using test:
[root@oel01db Shell-Scripting]# test -f /etc/passwd
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# echo $?
0
[root@oel01db Shell-Scripting]#
Both return exit code 0, which means true.
3. Using test in Scripts
Option A — Using test
if test -f /etc/passwd; then
echo "File exists"
fi
Option B — Using [ ]
if [ -f /etc/passwd ]; then
echo "File exists"
fi
Both are equivalent.
4. Simple Examples
Using test
[root@oel01db Shell-Scripting]# cat 01-test.sh
if test -f foo.jpg ; then
echo "foo.jpg exists"
fi
[root@oel01db Shell-Scripting]# touch foo.jpg
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# bash 01-test.sh
foo.jpg exists
[root@oel01db Shell-Scripting]#
Using [ ]
[root@oel01db Shell-Scripting]# cat 01-test\[.sh
if [ -f foo.jpg ] ; then
echo "foo.jpg exists"
fi
[root@oel01db Shell-Scripting]# bash 01-test\[.sh
foo.jpg exists
[root@oel01db Shell-Scripting]#
Using [[ ]]
[root@oel01db Shell-Scripting]# cat 01-test\[[.sh
if [[ -f foo.jpg ]] ; then
echo "foo.jpg exists"
fi
[root@oel01db Shell-Scripting]# bash 01-test\[[.sh
foo.jpg exists
[root@oel01db Shell-Scripting]#
5. Why [[ ]] Is Different
[root@oel01db Shell-Scripting]# type -a '[['
[[ is a shell keyword
[root@oel01db Shell-Scripting]#
[[ ]] is not a command — it is special Bash syntax.
Because of this, Bash treats everything inside [[ ]] differently.
Key advantages
-
No word splitting
-
No pathname expansion
-
Safer handling of spaces
-
Built-in pattern matching
-
Built-in regex support
-
Logical operators allowed (
&&,||)
6. Word Splitting Problem with [ ]
Consider this example:
[root@oel01db Shell-Scripting]# cat test01.sh
#!/bin/bash
my_file="my data.txt"
touch "$my_file"
echo "Testing with [ ]..."
if [ -f $my_file ]; then
echo "Success: File exists!"
fi
Run it:
[root@oel01db Shell-Scripting]# bash test01.sh
Testing with [ ]...
test01.sh: line 9: [: my: binary operator expected
[root@oel01db Shell-Scripting]#
Why it fails:
The shell splits $my_file into:
my
data.txt
So the command becomes:
[ -f my data.txt ]
Which confuses the [ command.
7. Why [[ ]] Works
[root@oel01db Shell-Scripting]# cat test02.sh
#!/bin/bash
my_file="my data.txt"
touch "$my_file"
echo "Testing with [ ]..."
if [[ -f $my_file ]]; then
echo "Success: File exists!"
fi
Run it:
[root@oel01db Shell-Scripting]# bash test02.sh
Testing with [ ]...
Success: File exists!
[root@oel01db Shell-Scripting]#
Why it works:
Inside [[ ]]:
-
Word splitting does not occur
8. Pattern Matching with [[ ]]
The double bracket [[ ... ]] supports pattern matching.
Example:
[[ $name == admin* ]]
This checks whether $name starts with "admin".
9. Regex Matching Example
[root@oel01db Shell-Scripting]# cat 03-pattern-match.sh
email=$1
if [[ $email =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
echo "$1 is a valid email format."
else
echo "$1 is no a valid email format."
fi
[root@oel01db Shell-Scripting]#
Test it:
[root@oel01db Shell-Scripting]# bash 03-pattern-match.sh mahi#gmail.com
mahi#gmail.com is no a valid email format.
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# bash 03-pattern-match.sh mahi@gmail.com
mahi@gmail.com is a valid email format.
[root@oel01db Shell-Scripting]#
Important note:
When using regex variables, do not quote the regex variable, or Bash will treat it as a string instead of a pattern.
10. File Pattern Matching Example
[root@oel01db Shell-Scripting]# cat 03-pattern-02.sh
#!/bin/bash
shopt -s nullglob
backup_dir="/root/Shell-Scripting/backup"
mkdir -p $backup_dir
for filename in *; do
if [[ $filename == *.sh ]] || [[ $filename == *.jpg ]]; then
echo "Moving file: $filename to $backup_dir"
mv "$filename" "$backup_dir"
fi
done
[root@oel01db Shell-Scripting]#
Run it:
[root@oel01db Shell-Scripting]# bash 03-pattern-02.sh
Moving file: 01-test.sh to /root/Shell-Scripting/backup
Moving file: 01-test[.sh to /root/Shell-Scripting/backup
Moving file: 1.jpg to /root/Shell-Scripting/backup
Moving file: 22.jpg to /root/Shell-Scripting/backup
[root@oel01db Shell-Scripting]#
11. Why It Fails with [ ]
[root@oel01db Shell-Scripting]# cat 03-pattern-02.sh
#!/bin/bash
shopt -s nullglob
backup_dir="/root/Shell-Scripting/backup"
mkdir -p $backup_dir
for filename in *; do
if [ $filename == *.sh ] || [ $filename == *.jpg ]; then
echo "Moving file: $filename to $backup_dir"
mv "$filename" "$backup_dir"
fi
done
[root@oel01db Shell-Scripting]#
Run it:
[root@oel01db Shell-Scripting]# bash 03-pattern-02.sh
03-pattern-02.sh: line 6: [: too many arguments
03-pattern-02.sh: line 6: [: too many arguments
Why it fails:
The shell expands *.sh before [ ] runs.
Example expansion:
[ a.sh == a.sh b.sh c.sh ]
Since [ ] can only compare two arguments, it fails.
But [[ ]] keeps the pattern unexpanded and performs pattern matching.
12. Numeric Comparisons in Bash
Consider this script:
[root@oel01db Shell-Scripting]# cat 01-check-number.sh
#!/usr/bin/env bash
a=9
b=10
if [[ $b > $a ]]; then
echo "$b is greater than $a"
else
echo "uh oh"
fi
Run it:
[root@oel01db Shell-Scripting]# bash 01-check-number.sh
uh oh
Why?
Because > performs string comparison, not numeric comparison.
13. Correct Numeric Comparison
Use numeric operators:
[root@oel01db Shell-Scripting]# cat 01-check-number.sh
#!/usr/bin/env bash
a=9
b=10
if [[ $b -gt $a ]]; then
echo "$b is greater than $a"
else
echo "uh oh"
fi
Output:
10 is greater than 9
Numeric comparison operators
| Operator | Meaning |
|---|---|
| -eq | equal |
| -ne | not equal |
| -gt | greater than |
| -lt | less than |
| -ge | greater or equal |
| -le | less or equal |
14. Arithmetic Evaluation Using (( ))
You can also write numeric comparisons like this:
[root@oel01db Shell-Scripting]# cat 01-check-number.sh
#!/usr/bin/env bash
a=9
b=10
if (( $b > $a )); then
echo "$b is greater than $a"
else
echo "uh oh"
fi
Run it:
[root@oel01db Shell-Scripting]# bash 01-check-number.sh
10 is greater than 9
[root@oel01db Shell-Scripting]#
15. Difference Between ( ) and (( ))
These two are completely different.
( ) → Subshell
Runs commands in a child shell.
Example:
(cd /tmp && ls)
Any directory change inside the subshell does not affect the parent shell.
(( )) → Arithmetic Evaluation
Used for math operations.
Example:
((count++))
((a = b + c))
((x > y))
Inside (( )):
-
Variables do not need
$ -
Only numeric evaluation occurs
Example:
(( b > a ))
16. Best Practice Summary
| Use Case | Recommended |
|---|---|
| Portable shell scripts | [ ] |
| Bash scripts | [[ ]] |
| Arithmetic operations | (( )) |
General guideline
For modern Bash scripting:
-
Use
[[ ... ]]for conditions -
Use
(( ... ))for arithmetic
17. Final Takeaway
Understanding these constructs helps you write cleaner, safer, and more powerful Bash scripts.
Quick recap:
| Syntax | Purpose |
|---|---|
test | Traditional POSIX test command |
[ ] | Same as test |
[[ ]] | Modern Bash conditional syntax |
(( )) | Arithmetic evaluation |
If you are writing Bash-specific scripts, prefer:
[[ condition ]]
(( arithmetic ))
No comments:
Post a Comment