Thursday, 12 March 2026

Understanding test, [ ], [[ ]], and (( )) in Bash

 

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:

CommandType
testbuiltin + 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

OperatorMeaning
-eqequal
-nenot equal
-gtgreater than
-ltless than
-gegreater or equal
-leless 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 CaseRecommended
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:

SyntaxPurpose
testTraditional 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

JFrog Artifactory - How to install

JFrog Artifactory OSS Installation Guide CentOS 9 + PostgreSQL 17 This guide provides a structured workflow to install JFrog Artifactory OSS...