TEST(1) General Commands Manual TEST(1)

test, [validate predicate

test [!] ( expr ) [{-a, -o} expr]…
test [!] string {=, !=} string, file {-ef, -nt, -ot} file [{-a, -o} expr]…
test [!] -t fd, {-n, -z} string, {-e, -s, -f, -d, -c, -b, -p, -S, -O, -G, -r, -w, -x, -u, -g, -k, -N} file, {-h, -L} path [{-a, -o} expr]…
test [!] integer {-lt, -le, -eq, -ne, -ge, -gt} integer [{-a, -o} expr]…
test [!] string [{-a, -o} expr]…
test

[ [!] ( expr ) [{-a, -o} expr]… ]
[ [!] string {=, !=} string, file {-ef, -nt, -ot} file [{-a, -o} expr]… ]
[ [!] -t fd, {-n, -z} string, {-e, -s, -f, -d, -c, -b, -p, -S, -O, -G, -r, -w, -x, -u, -g, -k, -N} file, {-h, -L} path [{-a, -o} expr]… ]
[ [!] integer {-lt, -le, -eq, -ne, -ge, -gt} integer [{-a, -o} expr]… ]
[ [!] string [{-a, -o} expr]… ]
[ ]

Exits with the result of the specified boolean expression. With no expression, exits false.

In chunked descending precedence, except all unary operators are equiprecedent; -ao left-associative.

True if:

expr )
expr

expr
expr is not true.

expr -a expr
Both expressions are.

expr -o expr
Either expression is.

string = string
The strings are identical.
string != string
The strings are not identical.
file -ef file
The files correspond to the same file — lie on the same device and point at the same i-node.
file-l -nt file-r
The modification time of file-l is earler than that of file-r.
file-l -ot file-r
The modification time of file-l is later than that of file-r.

fd
File descriptor fd corresponds to a teletype.

string
string is not empty.
string
string is empty.

file
file exists.
file
file's size is non-zero.

file
file is a regular file.
file
file is a directory.
file
file is a character device.
file
file is a block device.
file
file is a named pipe (FIFO).
file
file corresponds to a UNIX-domain socket.

path, -L path
path is a symbolic link.

file
file is owned by the process' effective user ID.
file
file is owned by the process' effective group ID.

file
file could be read by the process.
file
file could be written by the process.
file
file could be executed (searched) by the process.

file
file is set-user-ID.
file
file is set-group-ID.
file
file is sticky.

file
file's modification time is after its access time.

int-l -lt int-r
int-l < int-r
int-l -le int-r
int-lint-r
int-l -eq int-r
int-l = int-r
int-l -ne int-r
int-lint-r
int-l -ge int-r
int-lint-r
int-l -gt int-r
int-l > int-r

string
string

The expression evaluated true.
The expression evaluated false.
Syntax error in expression or non-integer passed to -t or an arithmetic operator.

A short, edited, extract from kernel-install(8):

#!/bin/sh

[ -z "$MACHINE_ID" ] && [ -f /etc/machine-id ] && read -r MACHINE_ID < /etc/machine-id
[ -z "$MACHINE_ID" ] && MACHINE_ID=Default

[ "$VERBOSE" -ge 3 ] && echo "Machine ID: $MACHINE_ID"

for suff in "$MACHINE_ID" "Default" "loader/entries"; do
    for pref in "/efi" "/boot" "/boot/efi"; do
        if [ -d "$pref/$suff" ]; then
            BOOT_ROOT="$pref"
            break 2
        fi
    done
done


if [ -z "$layout" ]; then
    if [ -d "$BOOT_ROOT/$MACHINE_ID" ]; then
        layout="bls-efi"
    else
        layout="legacy"
    fi
fi

expr(1), access(2), lstat(2), stat(2), isatty(3), inode(7)

Conforms to IEEE Std 1003.1-2008 (“POSIX.1”); -O, -G, -k, -ef, -nt, and -ot are extensions; -k originates from CB-UNIX, -N from bash(1), the rest from the KornShell,

The standard marks () and -ao as obsolete, and for good reason — the expression grammar is very loose and easy to throw off with malicious input. Chain multiple test invocations with && and || instead, though be wary of precedence (rather, the comparative lack thereof without an explicit {}).

Appears prototypically in Version 2 AT&T UNIX as if(I):

if expr command [ arg1 ... ]
Supporting exprs of
-r file
true if the file exists and is readable.
-w file
true if the file exists and is writable
-c file
true if the file either exists and is writable, or does not exist and is creatable.
= s2
true if the strings s1 and s2 are equal.
!= s2
true if the strings s1 and s2 are not equal.
grouped together with
!
unary negation operator
-a
binary and operator
-o
binary or operator
( expr )
parentheses for grouping.
With all sans -c as present-day, including -ao precedence.

The BUGS state:

"-c" always indicates the file is creatable, even if it isn't.

Version 3 AT&T UNIX removes -c.

Version 5 AT&T UNIX adds an expr of

command
The bracketed command is executed to obtain the exit status. Status 0 is considered true. The command must not be another if.
command taking arguments is undocumented. The if exclusion would hint at funny business, but there is none — everything up to the } is passed to execv(2) in the child, with the period-appropriate PATH emulation. This makes
if { whatever a b c } something q w e
essentially congruent with modern
if whatever a b c; then something q w e; fi

Version 7 AT&T UNIX makes if a reserved word in the shell, as present-day, and replaces if(I) with test supporting (), !, -ao, =, !=, -tnzsfdrw, -lt, -le, -eq, -ne, -ge, -gt, and the plain string. -t, when not followed by an argument, defaults to -t 1. -lt, -le, -eq, -ne, -ge, -gt, instead of integers, can be provided with -l string, resolving to the length of string.

CB-UNIX at or before version 2.3 adds -xcbugk and drops -l.

CB-UNIX was, among others, the basis for AT&T System III UNIX, which sees the same implementation, but, curiously, only as a sh(1) built-in.

AT&T System V Release 1 UNIX adds -p. AT&T System V Release 4 UNIX adds -h, alters -f to match any non-directories if /usr/ucb is in the PATH.

4.4BSD adds all CB-UNIX operators, -eph, and &| as aliases for -ao to a Version 7 AT&T UNIX base.

Version 7 AT&T UNIX accepts being called as [, undocumented in the manual, but only if it takes up the whole argument 0 (is not preceded by a path).

CB-UNIX adds the [ expr ] syntax to the manual.

4.4BSD checks only the final character.

IEEE Std 1003.1-2008 (“POSIX.1”) requires checking the basename, previous standards are unclear.

December 8, 2022 voreutils pre-v0.0.0-latest