test

test evaluates a single condition and reports whether it is true or false. It prints nothing — the answer is its exit status: 0 for true, 1 for false. That makes it the command a shell's if, while, &&, and || are built on.

test expression
[ expression ]

The two forms are the same command. [ is test under another name; when invoked as [, it requires a closing ] as its last argument. [ -f notes.txt ] and test -f notes.txt are identical.

$ test -f notes.txt && echo "the file exists"

File tests #

Existence and type #

TestTrue when the file…
-e FILEexists.
-f FILEexists and is a regular file.
-d FILEexists and is a directory.
-L FILE, -h FILEexists and is a symbolic link.
-p FILEexists and is a named pipe (FIFO).
-S FILEexists and is a socket.
-b FILEexists and is a block device.
-c FILEexists and is a character device.
-s FILEexists and is not empty.

Access #

These three tests ask whether you may actually use the file:

TestTrue when…
-r FILEyou may read the file.
-w FILEyou may write the file.
-x FILEyou may execute the file, or search the directory.

On Peios these are real access checks. test asks the kernel whether your token is granted the right in question — a live check against the file's security descriptor, the same decision any actual read, write, or execute would face. The answer is therefore honest: test -w FILE is true exactly when a write would be permitted.

-x on a regular file means "you are permitted to execute it" — which is a different question from whether the file is an executable. A file can be runnable code and still fail -x for you, and the two are decided separately; see Access decisions.

Comparing two files #

TestTrue when…
FILE1 -nt FILE2FILE1 is newer than FILE2.
FILE1 -ot FILE2FILE1 is older than FILE2.
FILE1 -ef FILE2both name the same file (same device and inode).

Other file tests #

TestTrue when the file…
-t FDfile descriptor FD is open on a terminal.
-N FILEhas been modified since it was last read.
-O FILE, -G FILEcarries an owner-id / group-id field matching the caller's.
-g FILE, -u FILE, -k FILEhas its set-group-id, set-user-id, or sticky inode bit set.

The last two rows probe decorative inode fields. The Peios access model does not consult them — they are stored on the inode and reported for completeness, but they carry no authority. For a true picture of what may be done to a file, use the access tests -r, -w, -x above, not -O, -g, or -u.

String tests #

TestTrue when…
-n STRINGSTRING is not empty. A bare STRING means the same.
-z STRINGSTRING is empty.
STRING1 = STRING2the strings are equal.
STRING1 != STRING2the strings are unequal.
STRING1 < STRING2STRING1 sorts before STRING2.
STRING1 > STRING2STRING1 sorts after STRING2.

Integer comparisons #

TestTrue when…
A -eq BA equals B.
A -ne BA does not equal B.
A -lt BA is less than B.
A -le BA is less than or equal to B.
A -gt BA is greater than B.
A -ge BA is greater than or equal to B.

Combining conditions #

FormResult
! EXPRESSIONThe negation.
( EXPRESSION )Grouping — escape the parentheses so the shell does not take them.
EXPR1 -a EXPR2True when both are true.
EXPR1 -o EXPR2True when either is true.

-a and -o are genuinely ambiguous to parse and are best avoided. Combine separate test calls with the shell's own && and || instead:

test -f notes.txt && test -r notes.txt

A note on shells #

Many command shells provide their own built-in test and [, and the shell's version is what runs when you type test at a prompt or in a script. A built-in may not behave as described here — in particular, the access tests -r, -w, and -x are the real access checks described above only when this command runs. To be certain you are running this command rather than the shell built-in, invoke it by its full path.

Exit status #

CodeMeaning
0The expression was true.
1The expression was false.
2The expression was malformed.

Edit this page