# test

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Evaluate a condition — a file check or a comparison — and report the result as an exit status.

`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](/peios/security-fundamentals/security-descriptors/overview.md), 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](/peios/security-fundamentals/access-decisions/overview.md).

### 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
```

## 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

| Code | Meaning |
|---|---|
| `0` | The expression was true. |
| `1` | The expression was false. |
| `2` | The expression was malformed. |

Related content:

- [Output and evaluation](/peios/using-peios/peiosutils/output-and-evaluation/overview.md)
- [Access decisions](/peios/security-fundamentals/access-decisions/overview.md)
- [Security descriptors](/peios/security-fundamentals/security-descriptors/overview.md)
