|

Linux CLI 60 ๐Ÿง test command in shell scripts

#!/bin/bash

# Numeric comparison
a=5
b=10
if test $a -lt $b; then
    echo "$a is less than $b"
fi

# String comparison
str1="hello"
str2="world"
if [ "$str1" != "$str2" ]; then
    echo "Strings are not equal"
fi

# File test
if test -f /etc/passwd; then
    echo "/etc/passwd is a regular file"
fi

# Logical AND
if test $a -eq 5 && $b -eq 10; then
    echo "Both conditions are true"
fi

# String length
str="Alibaba Cloud"
if [ -n "$str" ]; then
    echo "String is not empty"
fi

The test command is the engine behind every if condition you’ve written so far. [ ... ] is actually a synonym for test โ€” they do exactly the same thing. Understanding test directly makes your scripts clearer and helps you avoid quoting bugs.

Key point: test evaluates a conditional expression and returns an exit status โ€” 0 for true, 1 for false. [ ... ] is the same command with a required closing bracket. [[ ... ]] is bash’s extended version with more features.


a – test command part 1

test is used to evaluate conditional expressions. It returns an exit status based on whether the expression evaluates to true or false. It can be used in if statements, loops, and case statements.

Syntax:

test EXPRESSION
[ EXPRESSION ]

Both forms are identical. [ is literally a command โ€” /usr/bin/[ โ€” that requires a closing ].

Common uses:

CategoryOperators
Numeric comparison-eq, -ne, -lt, -le, -gt, -ge
String comparison=, !=
File tests-e, -f, -d, -r, -w, -x
Logical operators!, -a, -o, &&, ||
String length-z, -n

Numeric comparisons:

OperatorMeaning
-eqEqual
-neNot equal
-ltLess than
-leLess than or equal
-gtGreater than
-geGreater than or equal
a=5
b=10

if test $a -lt $b; then
    echo "$a is less than $b"
fi
[ 5 is less than 10 ]

if [ $a -eq 5 ]; then
    echo "a is 5"
fi
[ a is 5 ]

if [ $b -ge 10 ]; then
    echo "b is at least 10"
fi
[ b is at least 10 ]

String comparisons:

OperatorMeaning
=Equal
!=Not equal
<Less than (ASCII)
>Greater than (ASCII)
str1="hello"
str2="world"

if [ "$str1" != "$str2" ]; then
    echo "Strings are not equal"
fi
[ Strings are not equal ]

if [ "$str1" = "hello" ]; then
    echo "Match"
fi
[ Match ]

test vs [ ] vs [[ ]]:

FormExampleNotes
testtest -f filePOSIX, no brackets
[ ][ -f file ]POSIX, needs spaces
[[ ]][[ -f file ]]Bash only, more features

All three do the same basic thing:

test -f /etc/passwd && echo "yes"
[ -f /etc/passwd ] && echo "yes"
[[ -f /etc/passwd ]] && echo "yes"

Checking the result directly:

$ test 5 -lt 10
$ echo $?
[ 0 ]

$ test 5 -gt 10
$ echo $?
[ 1 ]

test doesn’t print anything โ€” it just sets $?.

Using test with && and ||:

$ test -f /etc/passwd && echo "exists" || echo "missing"
[ exists ]

$ test -d /etc/passwd && echo "dir" || echo "not a dir"
[ not a dir ]

Why use test instead of [ ]?

  • Slightly clearer in some scripts (no brackets to balance)
  • Useful when [ conflicts with glob characters
  • Shows what’s really happening โ€” [ is just a command

Why use [ ] instead?

  • More readable โ€” looks like a condition
  • The standard idiom in shell scripting
  • Works identically

Tip: Use [ ] for readability in most scripts. Know that test exists so you understand what [ really is.

Spaces matter:

# โœ… Correct
[ -f file ]
test -f file

# โŒ Wrong โ€” [ is a command, needs spaces
[-f file]         # "command not found: [-f"
[ -f file]        # missing closing ]

b – test command part 2

File tests:

OperatorTrue if
-e FILEFile exists
-f FILERegular file
-d FILEDirectory
-r FILEReadable
-w FILEWritable
-x FILEExecutable
-s FILENon-empty
-L FILESymlink
-p FILENamed pipe
-S FILESocket
-b FILEBlock device
-c FILECharacter device

Examples:

# Exists
if test -e /etc/passwd; then
    echo "exists"
fi
[ exists ]

# Regular file
if test -f /etc/passwd; then
    echo "/etc/passwd is a regular file"
fi
[ /etc/passwd is a regular file ]

# Directory
if test -d /etc; then
    echo "/etc is a directory"
fi
[ /etc is a directory ]

# Readable
if test -r /etc/passwd; then
    echo "readable"
fi
[ readable ]

# Writable
if test -w /tmp; then
    echo "writable"
fi
[ writable ]

# Executable
if test -x /usr/bin/ls; then
    echo "executable"
fi
[ executable ]

# Non-empty
if test -s /etc/passwd; then
    echo "non-empty"
fi
[ non-empty ]

File comparison operators:

OperatorTrue if
FILE1 -nt FILE2FILE1 is newer than FILE2
FILE1 -ot FILE2FILE1 is older than FILE2
FILE1 -ef FILE2Same file (same inode)
if [ file1.txt -nt file2.txt ]; then
    echo "file1 is newer"
fi

if [ /etc/passwd -ef /etc/passwd ]; then
    echo "same file"
fi

Logical operators:

OperatorMeaningStatus
!NOTCurrent
-aANDDeprecated
-oORDeprecated
&&ANDPreferred
||ORPreferred
# NOT
if [ ! -f /nonexistent ]; then
    echo "File not found"
fi
[ File not found ]

# AND (deprecated inside [ ])
if [ -f /etc/passwd -a -r /etc/passwd ]; then
    echo "exists and readable"
fi

# AND (preferred โ€” separate tests)
if [ -f /etc/passwd ] && [ -r /etc/passwd ]; then
    echo "exists and readable"
fi
[ exists and readable ]

# OR
if [ -f /etc/hosts ] || [ -f /etc/passwd ]; then
    echo "at least one exists"
fi
[ at least one exists ]

โš ๏ธ Warning: -a and -o are deprecated and ambiguous when mixed with file tests. Use && and || between separate [ ] blocks, or switch to [[ ]].

In [[ ]], combine inside:

if [[ -f /etc/passwd && -r /etc/passwd ]]; then
    echo "exists and readable"
fi

String length operators:

OperatorTrue if
-z STRINGString is null (zero length)
-n STRINGString is not null
str="Alibaba Cloud"

if [ -n "$str" ]; then
    echo "String is not empty"
fi
[ String is not empty ]

empty=""
if [ -z "$empty" ]; then
    echo "String is empty"
fi
[ String is empty ]

Always quote strings with -z and -n:

# โœ… Correct
[ -z "$var" ]
[ -n "$var" ]

# โŒ Wrong โ€” breaks if $var is empty or has spaces
[ -z $var ]
[ -n $var ]

Checking multiple things at once:

# File exists AND is readable
if [ -f "$file" ] && [ -r "$file" ]; then
    echo "Can read $file"
fi

# Directory OR file
if [ -d "$path" ] || [ -f "$path" ]; then
    echo "Path exists"
fi

# NOT empty AND NOT a directory
if [ -n "$name" ] && [ ! -d "$name" ]; then
    echo "Valid name"
fi

The square brackets [ ] vs test:

They are the same command. [ is a symlink to test that expects a closing ]:

$ ls -l /usr/bin/[
lrwxrwxrwx 1 root root 4 ... /usr/bin/[ -> test

$ test -f /etc/passwd; echo $?
[ 0 ]

$ [ -f /etc/passwd ]; echo $?
[ 0 ]

[ ] vs [[ ]] โ€” a quick reminder:

Feature[ ] / test[[ ]]
POSIXโœ…โŒ
Quote variablesRequiredOptional
&& / || insideโŒโœ…
Regex =~โŒโœ…
Glob ==โŒโœ…
Word splittingYesNo
< / >Escape neededWorks bare
# [ ] โ€” quote everything
if [ "$name" = "Alice" ]; then ...; fi

# [[ ]] โ€” no quoting needed
if [[ $name == "Alice" ]]; then ...; fi

# [[ ]] โ€” logical operators inside
if [[ $a -gt 0 && $b -gt 0 ]]; then ...; fi

# [[ ]] โ€” regex
if [[ $email =~ @.*\.com$ ]]; then ...; fi

Complete expression reference:

CategoryExpressionMeaning
Numeric$a -eq $bEqual
Numeric$a -ne $bNot equal
Numeric$a -lt $bLess than
Numeric$a -le $bLess or equal
Numeric$a -gt $bGreater than
Numeric$a -ge $bGreater or equal
String$a = $bEqual
String$a != $bNot equal
String-z $aEmpty
String-n $aNon-empty
File-e $fExists
File-f $fRegular file
File-d $fDirectory
File-r $fReadable
File-w $fWritable
File-x $fExecutable
File-s $fNon-empty
File-L $fSymlink
Compare$f1 -nt $f2Newer than
Compare$f1 -ot $f2Older than
Compare$f1 -ef $f2Same file
Logic! EXPRNOT
LogicEXPR1 && EXPR2AND
LogicEXPR1 || EXPR2OR

Complete Example Session

# ============================================
# PART 1: NUMERIC COMPARISON
# ============================================

$ a=5
$ b=10
$ if test $a -lt $b; then echo "$a < $b"; fi
[ 5 < 10 ]

$ if test $a -eq 5; then echo "a is 5"; fi
[ a is 5 ]

$ test 5 -gt 10
$ echo $?
[ 1 ]

# ============================================
# PART 2: STRING COMPARISON
# ============================================

$ str1="hello"
$ str2="world"
$ if [ "$str1" != "$str2" ]; then echo "not equal"; fi
[ not equal ]

$ if [ "$str1" = "hello" ]; then echo "match"; fi
[ match ]

# ============================================
# PART 3: FILE TESTS
# ============================================

$ if test -f /etc/passwd; then echo "regular file"; fi
[ regular file ]

$ if test -d /etc; then echo "directory"; fi
[ directory ]

$ if test -r /etc/passwd; then echo "readable"; fi
[ readable ]

$ if test -x /usr/bin/ls; then echo "executable"; fi
[ executable ]

$ if test -s /etc/passwd; then echo "non-empty"; fi
[ non-empty ]

# ============================================
# PART 4: LOGICAL AND
# ============================================

$ a=5
$ b=10
$ if test $a -eq 5 && $b -eq 10; then echo "both true"; fi
[ both true ]

$ if [ $a -gt 0 ] && [ $b -gt 0 ]; then echo "both positive"; fi
[ both positive ]

# ============================================
# PART 5: LOGICAL OR
# ============================================

$ if [ -f /etc/hosts ] || [ -f /etc/passwd ]; then echo "one exists"; fi
[ one exists ]

# ============================================
# PART 6: LOGICAL NOT
# ============================================

$ if [ ! -f /nonexistent ]; then echo "not found"; fi
[ not found ]

# ============================================
# PART 7: STRING LENGTH
# ============================================

$ str="Alibaba Cloud"
$ if [ -n "$str" ]; then echo "not empty"; fi
[ not empty ]

$ empty=""
$ if [ -z "$empty" ]; then echo "empty"; fi
[ empty ]

# ============================================
# PART 8: TEST VS [ ] VS [[ ]]
# ============================================

$ test -f /etc/passwd && echo "yes"
[ yes ]

$ [ -f /etc/passwd ] && echo "yes"
[ yes ]

$ [[ -f /etc/passwd ]] && echo "yes"
[ yes ]

# ============================================
# PART 9: REGEX WITH [[ ]]
# ============================================

$ email="user@example.com"
$ if [[ $email =~ @.*\.com$ ]]; then echo "valid"; fi
[ valid ]

# ============================================
# PART 10: GLOB PATTERN WITH [[ ]]
# ============================================

$ file="report.txt"
$ if [[ $file == *.txt ]]; then echo "text file"; fi
[ text file ]

# ============================================
# PART 11: NUMERIC WITH [[ ]]
# ============================================

$ a=5
$ b=10
$ if [[ $a -lt $b ]]; then echo "less"; fi
[ less ]

$ if [[ $a -lt $b && $b -eq 10 ]]; then echo "both"; fi
[ both ]

# ============================================
# PART 12: FILE COMPARISON
# ============================================

$ touch /tmp/old.txt
$ sleep 1
$ touch /tmp/new.txt
$ if [ /tmp/new.txt -nt /tmp/old.txt ]; then echo "new is newer"; fi
[ new is newer ]

# ============================================
# PART 13: COMBINED CONDITIONS
# ============================================

$ file="/etc/passwd"
$ if [ -f "$file" ] && [ -r "$file" ]; then
>     echo "Can read $file"
> fi
[ Can read /etc/passwd ]

# ============================================
# PART 14: NEGATION WITH FILES
# ============================================

$ if [ ! -d /nonexistent ]; then
>     echo "not a directory"
> fi
[ not a directory ]

# ============================================
# PART 15: FULL SCRIPT EXAMPLE
# ============================================

$ cat > check.sh << 'EOF'
#!/bin/bash

a=5
b=10

if test $a -lt $b; then
    echo "$a is less than $b"
fi

str1="hello"
str2="world"
if [ "$str1" != "$str2" ]; then
    echo "Strings are not equal"
fi

if test -f /etc/passwd; then
    echo "/etc/passwd is a regular file"
fi

if test $a -eq 5 && $b -eq 10; then
    echo "Both conditions are true"
fi

str="Alibaba Cloud"
if [ -n "$str" ]; then
    echo "String is not empty"
fi
EOF

$ chmod +x check.sh
$ ./check.sh
[ 5 is less than 10 ]
[ Strings are not equal ]
[ /etc/passwd is a regular file ]
[ Both conditions are true ]
[ String is not empty ]

Quick Reference

test / [ ] / [[ ]]

FormExamplePortability
testtest -f filePOSIX
[ ][ -f file ]POSIX
[[ ]][[ -f file ]]Bash, ksh, zsh

Numeric Operators

OperatorMeaning
-eqEqual
-neNot equal
-ltLess than
-leLess than or equal
-gtGreater than
-geGreater than or equal

String Operators

OperatorMeaning
=Equal
!=Not equal
-zEmpty
-nNon-empty
<Less (ASCII)
>Greater (ASCII)

File Tests

OperatorTrue if
-eExists
-fRegular file
-dDirectory
-rReadable
-wWritable
-xExecutable
-sNon-empty
-LSymlink
-pNamed pipe
-SSocket

File Comparison

OperatorTrue if
f1 -nt f2f1 newer than f2
f1 -ot f2f1 older than f2
f1 -ef f2Same file

Logical Operators

OperatorMeaningStatus
!NOTCurrent
-aANDDeprecated
-oORDeprecated
&&ANDPreferred
||ORPreferred

[ ] vs [[ ]

Feature[ ][[ ]]
POSIXโœ…โŒ
Quote varsRequiredOptional
&& insideโŒโœ…
|| insideโŒโœ…
Regex =~โŒโœ…
Glob ==โŒโœ…
</>EscapeBare

Best Practices

โœ… Do This:

# Quote variables in [ ]
if [ "$name" = "Alice" ]; then ...; fi       # โœ…

# Use -z and -n with quotes
if [ -z "$var" ]; then ...; fi                # โœ…
if [ -n "$var" ]; then ...; fi                # โœ…

# Separate tests with &&
if [ -f "$f" ] && [ -r "$f" ]; then ...; fi   # โœ…

# Use [[ ]] in bash
if [[ $name == "Alice" ]]; then ...; fi       # โœ…

# Combine inside [[ ]]
if [[ -f "$f" && -r "$f" ]]; then ...; fi     # โœ…

# Use test for simple checks
test -f file && echo "ok"                     # โœ…

# Use -n instead of bare string
if [ -n "$str" ]; then ...; fi                # โœ…

# Use ! for negation
if [ ! -f "$f" ]; then ...; fi                # โœ…

โŒ Don’t Do This:

# Don't forget spaces
[-f file]                                     # โŒ command not found
[ -f file]                                    # โŒ missing ]

# Don't use -a and -o
[ -f file -a -r file ]                        # โš ๏ธ  deprecated
[ -f file ] && [ -r file ]                    # โœ…

# Don't leave strings unquoted
[ -z $var ]                                   # โŒ breaks on empty
[ -z "$var" ]                                 # โœ…

# Don't use = for numbers
[ "$a" = 5 ]                                  # โš ๏ธ  string compare
[ "$a" -eq 5 ]                                # โœ…

# Don't use -eq for strings
[ "$name" -eq "Alice" ]                       # โŒ error
[ "$name" = "Alice" ]                         # โœ…

# Don't use > without escaping in [ ]
[ "$a" > "$b" ]                               # โŒ redirection
[[ "$a" > "$b" ]]                             # โœ…

# Don't test a variable without quotes
if [ $name = "" ]; then ...; fi               # โŒ breaks if unset
if [ -z "$name" ]; then ...; fi               # โœ…

Common Pitfalls

PitfallProblemSolution
Missing spacescommand not found[ -f file ]
Missing ]Syntax errorAdd closing bracket
Unquoted variableBreaks on emptyQuote: "$var"
-a / -oDeprecatedUse && / ||
= for numbersString compareUse -eq
-eq for stringsErrorUse =
> unescapedRedirection\> or [[ ]]
test with [[ featuresNot supportedUse [[ ]]

Real-World Examples

1. Numeric Comparison

#!/bin/bash
a=5
b=10
if test $a -lt $b; then
    echo "$a is less than $b"
fi
[ 5 is less than 10 ]

2. String Comparison

#!/bin/bash
str1="hello"
str2="world"
if [ "$str1" != "$str2" ]; then
    echo "Strings are not equal"
fi
[ Strings are not equal ]

3. File Test

#!/bin/bash
if test -f /etc/passwd; then
    echo "/etc/passwd is a regular file"
fi
[ /etc/passwd is a regular file ]

4. Logical AND

#!/bin/bash
a=5
b=10
if test $a -eq 5 && $b -eq 10; then
    echo "Both conditions are true"
fi
[ Both conditions are true ]

5. String Length

#!/bin/bash
str="Alibaba Cloud"
if [ -n "$str" ]; then
    echo "String is not empty"
fi
[ String is not empty ]

6. Check If File Exists

#!/bin/bash
file="$1"
if [ ! -f "$file" ]; then
    echo "Error: $file not found" >&2
    exit 1
fi
echo "File exists"

7. Check If Directory

#!/bin/bash
dir="$1"
if [ -d "$dir" ]; then
    echo "Directory exists"
else
    echo "Not a directory"
fi

8. Check Readability

#!/bin/bash
if [ -r "$1" ]; then
    cat "$1"
else
    echo "Cannot read $1" >&2
    exit 1
fi

9. Check Executability

#!/bin/bash
if [ ! -x "$1" ]; then
    echo "$1 is not executable" >&2
    exit 1
fi
"$1"

10. Empty Variable Check

#!/bin/bash
if [ -z "$1" ]; then
    echo "Usage: $0 <arg>" >&2
    exit 1
fi
echo "Arg: $1"

11. Non-Empty Variable Check

#!/bin/bash
read -p "Name: " name
if [ -n "$name" ]; then
    echo "Hello, $name"
else
    echo "No name entered"
fi

12. Both File AND Readable

#!/bin/bash
file="/etc/passwd"
if [ -f "$file" ] && [ -r "$file" ]; then
    echo "Can read $file"
    head -3 "$file"
fi

13. Either File OR Backup

#!/bin/bash
if [ -f config.txt ] || [ -f config.bak ]; then
    echo "Config found"
else
    echo "No config"
fi

14. File Newer Than

#!/bin/bash
if [ "$1" -nt "$2" ]; then
    echo "$1 is newer than $2"
else
    echo "$1 is older or same"
fi

15. Same File Check

#!/bin/bash
if [ "$1" -ef "$2" ]; then
    echo "Same file"
else
    echo "Different files"
fi

16. Regex Email Validation

#!/bin/bash
read -p "Email: " email
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Valid email"
else
    echo "Invalid email"
fi

17. Glob Pattern Match

#!/bin/bash
file="report.txt"
if [[ "$file" == *.txt ]]; then
    echo "Text file"
fi

18. Numeric Range

#!/bin/bash
read -p "Age: " age
if [[ "$age" -ge 18 && "$age" -le 65 ]]; then
    echo "Working age"
else
    echo "Outside range"
fi

19. Multiple File Checks

#!/bin/bash
for f in /etc/passwd /etc/hosts /etc/group; do
    if [ -f "$f" ]; then
        echo "โœ“ $f"
    else
        echo "โœ— $f"
    fi
done

20. Validate Numeric Input

#!/bin/bash
read -p "Number: " n
if [[ "$n" =~ ^-?[0-9]+$ ]]; then
    echo "Valid integer"
else
    echo "Not an integer"
fi

Visual: test / [ ] / [[ ]

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           test COMMAND                       โ”‚
โ”‚                                              โ”‚
โ”‚  test -f /etc/passwd                         โ”‚
โ”‚      โ”‚                                       โ”‚
โ”‚      โ””โ”€โ”€โ†’ returns 0 (true) or 1 (false)      โ”‚
โ”‚                                              โ”‚
โ”‚  No output โ€” just exit status                โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           [ ] โ€” same as test                 โ”‚
โ”‚                                              โ”‚
โ”‚  [ -f /etc/passwd ]                          โ”‚
โ”‚    โ”‚                  โ”‚                      โ”‚
โ”‚    โ””โ”€โ”€โ”€ command โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚
โ”‚                                              โ”‚
โ”‚  [ is a real command that requires ]         โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           [[ ]] โ€” bash extended              โ”‚
โ”‚                                              โ”‚
โ”‚  [[ -f /etc/passwd && -r /etc/passwd ]]      โ”‚
โ”‚                                              โ”‚
โ”‚  โ€ข No quoting needed                         โ”‚
โ”‚  โ€ข && and || work inside                     โ”‚
โ”‚  โ€ข Regex with =~                             โ”‚
โ”‚  โ€ข Glob with ==                              โ”‚
โ”‚  โ€ข Bash only                                 โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: test Categories

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           test categories                    โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  NUMERIC                               โ”‚  โ”‚
โ”‚  โ”‚  -eq  -ne  -lt  -le  -gt  -ge          โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  STRING                                โ”‚  โ”‚
โ”‚  โ”‚  =  !=  -z  -n  <  >                   โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  FILE                                  โ”‚  โ”‚
โ”‚  โ”‚  -e  -f  -d  -r  -w  -x  -s  -L       โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  FILE COMPARE                          โ”‚  โ”‚
โ”‚  โ”‚  -nt  -ot  -ef                         โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  LOGICAL                               โ”‚  โ”‚
โ”‚  โ”‚  !  -a  -o  &&  ||                     โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ConceptSyntaxExample
testtest EXPRtest -f file
Bracket form[ EXPR ][ -f file ]
Extended form[[ EXPR ]][[ -f file ]]
Numeric equal-eq[ $a -eq 5 ]
Numeric not equal-ne[ $a -ne 5 ]
Numeric less-lt[ $a -lt 5 ]
Numeric less or equal-le[ $a -le 5 ]
Numeric greater-gt[ $a -gt 5 ]
Numeric greater or equal-ge[ $a -ge 5 ]
String equal=[ "$a" = "$b" ]
String not equal!=[ "$a" != "$b" ]
String empty-z[ -z "$s" ]
String non-empty-n[ -n "$s" ]
File exists-e[ -e file ]
Regular file-f[ -f file ]
Directory-d[ -d dir ]
Readable-r[ -r file ]
Writable-w[ -w file ]
Executable-x[ -x file ]
Non-empty file-s[ -s file ]
Symlink-L[ -L link ]
Newer than-nt[ f1 -nt f2 ]
Older than-ot[ f1 -ot f2 ]
Same file-ef[ f1 -ef f2 ]
NOT![ ! -f f ]
AND&&[ -f f ] && [ -r f ]
OR||[ -f f ] || [ -f g ]

Key takeaways:

  • test evaluates a condition and returns an exit status โ€” 0 for true, 1 for false
  • [ ... ] is the same as test โ€” just more readable
  • [[ ... ]] is bash’s extended version with &&, ||, regex, and glob matching
  • Use numeric operators (-eq, -lt, etc.) for numbers
  • Use string operators (=, !=, -z, -n) for strings
  • Use file operators (-f, -d, -r, -x) to test files
  • Quote variables in [ ] โ€” unquoted empties cause errors
  • Use && and || between separate [ ] blocks โ€” avoid deprecated -a and -o
  • Use ! to negate a condition
  • Combine multiple tests with && (and) and || (or)
  • Prefer [[ ]] in bash when you need regex or logical operators inside
  • Spaces matter โ€” [ -f file ] not [-f file]
  • test returns a status, not output โ€” check it with $? or use it in if

Remember: test is the engine behind every if you’ve written. [ is just another name for it. Learn the operators by category โ€” numeric, string, file, logical โ€” and you’ll never fumble a condition again. Quote your variables,prefer [[ ]] in bash, and use &&/|| instead of -a/-o. Master test, and every decision your script makes becomes clear and correct.


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!