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 #
| Test |
True when the file… |
-e FILE |
exists. |
-f FILE |
exists and is a regular file. |
-d FILE |
exists and is a directory. |
-L FILE, -h FILE |
exists and is a symbolic link. |
-p FILE |
exists and is a named pipe (FIFO). |
-S FILE |
exists and is a socket. |
-b FILE |
exists and is a block device. |
-c FILE |
exists and is a character device. |
-s FILE |
exists and is not empty. |
Access #
These three tests ask whether you may actually use the file:
| Test |
True when… |
-r FILE |
you may read the file. |
-w FILE |
you may write the file. |
-x FILE |
you 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 #
| Test |
True when… |
FILE1 -nt FILE2 |
FILE1 is newer than FILE2. |
FILE1 -ot FILE2 |
FILE1 is older than FILE2. |
FILE1 -ef FILE2 |
both name the same file (same device and inode). |
Other file tests #
| Test |
True when the file… |
-t FD |
file descriptor FD is open on a terminal. |
-N FILE |
has been modified since it was last read. |
-O FILE, -G FILE |
carries an owner-id / group-id field matching the caller's. |
-g FILE, -u FILE, -k FILE |
has 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 #
| Test |
True when… |
-n STRING |
STRING is not empty. A bare STRING means the same. |
-z STRING |
STRING is empty. |
STRING1 = STRING2 |
the strings are equal. |
STRING1 != STRING2 |
the strings are unequal. |
STRING1 < STRING2 |
STRING1 sorts before STRING2. |
STRING1 > STRING2 |
STRING1 sorts after STRING2. |
Integer comparisons #
| Test |
True when… |
A -eq B |
A equals B. |
A -ne B |
A does not equal B. |
A -lt B |
A is less than B. |
A -le B |
A is less than or equal to B. |
A -gt B |
A is greater than B. |
A -ge B |
A is greater than or equal to B. |
Combining conditions #
| Form |
Result |
! EXPRESSION |
The negation. |
( EXPRESSION ) |
Grouping — escape the parentheses so the shell does not take them. |
EXPR1 -a EXPR2 |
True when both are true. |
EXPR1 -o EXPR2 |
True 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
Exit status #
| Code |
Meaning |
0 |
The expression was true. |
1 |
The expression was false. |
2 |
The expression was malformed. |