test / [ / [[ - Conditional Expressions

Evaluate conditional expressions.

Source: src/scripting/test_builtin.f90:execute_test_command

Synopsis

test expression
[ expression ]
[[ expression ]]

Description

Evaluates a conditional expression and returns an exit status of 0 (true) or 1 (false). The [ command is identical to test but requires a closing ]. The [[ command is an extended test with additional features.

File Tests

OperatorTrue if...
-e fileFile exists
-f fileRegular file exists
-d fileDirectory exists
-L fileSymbolic link exists
-h fileSymbolic link exists (same as -L)
-b fileBlock device exists
-c fileCharacter device exists
-p fileNamed pipe (FIFO) exists
-S fileSocket exists
-r fileFile is readable
-w fileFile is writable
-x fileFile is executable
-s fileFile has size > 0
-u fileSUID bit is set
-g fileSGID bit is set
-k fileSticky bit is set
-t fdFile descriptor is a terminal

String Tests

OperatorTrue if...
-z stringString length is zero
-n stringString length is non-zero
stringString is non-empty (implicit -n)
s1 = s2Strings are equal
s1 != s2Strings are not equal
s1 < s2s1 sorts before s2 (lexicographic)
s1 > s2s1 sorts after s2 (lexicographic)

Numeric Tests

OperatorTrue if...
n1 -eq n2Equal
n1 -ne n2Not equal
n1 -lt n2Less than
n1 -le n2Less than or equal
n1 -gt n2Greater than
n1 -ge n2Greater than or equal

File Comparison

OperatorTrue if...
f1 -nt f2f1 is newer than f2
f1 -ot f2f1 is older than f2
f1 -ef f2f1 and f2 are the same file (same inode)

Logical Operators (test / [)

OperatorMeaning
! exprLogical NOT
expr1 -a expr2Logical AND
expr1 -o expr2Logical OR
( expr )Grouping (requires escaping in [ ])

Extended Test [[ ]]

The [[ command provides additional features:

Pattern Matching

[[ $string == pattern ]]   # Glob pattern match
[[ $string != pattern ]]   # Glob pattern not match

Regex Matching

[[ $string =~ regex ]]     # POSIX extended regex
[[ $string !~ regex ]]     # Regex not match

Capture groups are stored in BASH_REMATCH:

  • ${BASH_REMATCH[0]} - Full match
  • ${BASH_REMATCH[1]} - First capture group
  • etc.

Logical Operators in [[

[[ expr1 && expr2 ]]   # Logical AND (short-circuit)
[[ expr1 || expr2 ]]   # Logical OR (short-circuit)
[[ ! expr ]]           # Logical NOT

Return Value

CodeMeaning
0Expression is true
1Expression is false
2Syntax error

Examples

File tests

if [ -f /etc/passwd ]; then
    echo "File exists"
fi

if [[ -d "$HOME" && -w "$HOME" ]]; then
    echo "Home exists and is writable"
fi

String tests

name="fortsh"

if [ -n "$name" ]; then
    echo "Name is set"
fi

if [ "$name" = "fortsh" ]; then
    echo "It's fortsh!"
fi

Numeric tests

count=5

if [ "$count" -gt 0 ]; then
    echo "Count is positive"
fi

if [[ $count -ge 1 && $count -le 10 ]]; then
    echo "Count is between 1 and 10"
fi

Regex matching

email="user@example.com"

if [[ $email =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]; then
    echo "Valid email format"
fi

# With capture groups
version="v1.2.3"
if [[ $version =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
    echo "Major: ${BASH_REMATCH[1]}"
    echo "Minor: ${BASH_REMATCH[2]}"
    echo "Patch: ${BASH_REMATCH[3]}"
fi

Pattern matching

file="script.sh"

if [[ $file == *.sh ]]; then
    echo "Shell script"
fi

if [[ $file == script.* ]]; then
    echo "A script file"
fi

Implementation Details

From test_builtin.f90:

! File test operators
case ("-e")
    call stat_file(path, stat_result, errno)
    result = (errno == 0)
case ("-f")
    result = is_regular_file(path)
case ("-d")
    result = is_directory(path)

The regex matching uses POSIX extended regular expressions:

! Extended test: regex match with =~
if (extended .and. operator == "=~") then
    call regex_match(left_operand, right_operand, matched, captures)
    if (matched) then
        ! Store captures in BASH_REMATCH array
        call set_bash_rematch(captures)
    end if
end if

Differences Between [ ] and [[ ]]

Feature[ ][[ ]]
Word splittingYesNo
Glob expansionYesNo
&& / ||Use -a / -oNative support
Pattern matchingNoYes (==, !=)
RegexNoYes (=~)
QuotingOften requiredLess strict

Related Commands

  • if - Conditional execution
  • case - Pattern matching

Notes

  • For portability, use [ ] with -a and -o operators
  • [[ ]] is preferred for complex conditions due to cleaner syntax
  • Always quote variables in [ ] to prevent word splitting: [ "$var" = "value" ]
  • In [[ ]], quoting is optional for variables: [[ $var == value ]]