TEST(1) fish-shell TEST(1)

# [NAME](#NAME)

test - perform tests on files and text

# [SYNOPSIS](#SYNOPSIS)

```
test [EXPRESSION]
[ [EXPRESSION] ]
```

# [DESCRIPTION](#DESCRIPTION)

NOTE: This page documents the fish builtin **test**. To see the documentation on any non-fish versions, use **command man test**.

**test** checks the given conditions and sets the exit status to 0 if they are true, 1 if they are false.

The first form (**test**) is preferred. For compatibility with other shells, the second form is available: a matching pair of square brackets (**\[ \[EXPRESSION] ]**).

When using a variable or command substitution as an argument with **test** you should almost always enclose it in double-quotes, as variables expanding to zero or more than one argument will most likely interact badly with **test**.

**Warning:**

For historical reasons, **test** supports the one-argument form (**test foo**), and this will also be triggered by e.g. **test -n $foo** if $foo is unset. We recommend you don't use the one-argument form and quote all variables or command substitutions used with **test**.

This confusing misfeature will be removed in future. **test -n** without any additional argument will be false, **test -z** will be true and any other invocation with exactly one or zero arguments, including **test -d** and **test "foo"** will be an error.

The same goes for **\[**, e.g. **\[ "foo" ]** and **\[ -d ]** will be errors.

This can be turned on already via the **test-require-arg** feature flag &lt;#featureflags&gt;, and will eventually become the default and then only option.

# [OPERATORS FOR FILES AND DIRECTORIES](#OPERATORS_FOR_FILES_AND_DIRECTORIES)

[**-b** *FILE*](#b)

Returns true if *FILE* is a block device.

[**-c** *FILE*](#c)

Returns true if *FILE* is a character device.

[**-d** *FILE*](#d)

Returns true if *FILE* is a directory.

[**-e** *FILE*](#e)

Returns true if *FILE* exists.

[**-f** *FILE*](#f)

Returns true if *FILE* is a regular file.

[**-g** *FILE*](#g)

Returns true if *FILE* has the set-group-ID bit set.

[**-G** *FILE*](#G)

Returns true if *FILE* exists and has the same group ID as the current user.

[**-k** *FILE*](#k)

Returns true if *FILE* has the sticky bit set. If the OS does not support the concept it returns false. See &lt;https://en.wikipedia.org/wiki/Sticky\_bit&gt;.

[**-L** *FILE*](#L)

Returns true if *FILE* is a symbolic link.

[**-O** *FILE*](#O)

Returns true if *FILE* exists and is owned by the current user.

[**-p** *FILE*](#p)

Returns true if *FILE* is a named pipe.

[**-r** *FILE*](#r)

Returns true if *FILE* is marked as readable.

[**-s** *FILE*](#s)

Returns true if the size of *FILE* is greater than zero.

[**-S** *FILE*](#S)

Returns true if *FILE* is a socket.

[**-t** *FD*](#t)

Returns true if the file descriptor *FD* is a terminal (TTY).

[**-u** *FILE*](#u)

Returns true if *FILE* has the set-user-ID bit set.

[**-w** *FILE*](#w)

Returns true if *FILE* is marked as writable; note that this does not check if the filesystem is read-only.

[**-x** *FILE*](#x)

Returns true if *FILE* is marked as executable.

# [OPERATORS TO COMPARE FILES AND DIRECTORIES](#OPERATORS_TO_COMPARE_FILES_AND_DIRECTORIES)

[*FILE1* **-nt** *FILE2*](#FILE1)

Returns true if *FILE1* is newer than *FILE2*, or *FILE1* exists and *FILE2* does not.

[*FILE1* **-ot** *FILE2*](#FILE1~2)

Returns true if *FILE1* is older than *FILE2*, or *FILE2* exists and *FILE1* does not.

[*FILE1* **-ef** *FILE1*](#FILE1~3)

Returns true if *FILE1* and *FILE2* refer to the same file.

# [OPERATORS FOR TEXT STRINGS](#OPERATORS_FOR_TEXT_STRINGS)

[*STRING1* **=** *STRING2*](#STRING1)

Returns true if the strings *STRING1* and *STRING2* are identical.

[*STRING1* **!=** *STRING2*](#STRING1~2)

Returns true if the strings *STRING1* and *STRING2* are not identical.

[**-n** *STRING*](#n)

Returns true if the length of *STRING* is non-zero.

[**-z** *STRING*](#z)

Returns true if the length of *STRING* is zero.

# [OPERATORS TO COMPARE AND EXAMINE NUMBERS](#OPERATORS_TO_COMPARE_AND_EXAMINE_NUMBERS)

[*NUM1* **-eq** *NUM2*](#NUM1)

Returns true if *NUM1* and *NUM2* are numerically equal.

[*NUM1* **-ne** *NUM2*](#NUM1~2)

Returns true if *NUM1* and *NUM2* are not numerically equal.

[*NUM1* **-gt** *NUM2*](#NUM1~3)

Returns true if *NUM1* is greater than *NUM2*.

[*NUM1* **-ge** *NUM2*](#NUM1~4)

Returns true if *NUM1* is greater than or equal to *NUM2*.

[*NUM1* **-lt** *NUM2*](#NUM1~5)

Returns true if *NUM1* is less than *NUM2*.

[*NUM1* **-le** *NUM2*](#NUM1~6)

Returns true if *NUM1* is less than or equal to *NUM2*.

Both integers and floating point numbers are supported.

# [OPERATORS TO COMBINE EXPRESSIONS](#OPERATORS_TO_COMBINE_EXPRESSIONS)

[*COND1* **-a** *COND2*](#COND1)

Returns true if both *COND1* and *COND2* are true.

[*COND1* **-o** *COND2*](#COND1~2)

Returns true if either *COND1* or *COND2* are true.

Expressions can be inverted using the **!** operator:

**!** *EXPRESSION*

Returns true if *EXPRESSION* is false, and false if *EXPRESSION* is true.

Expressions can be grouped using parentheses.

**(** *EXPRESSION* **)**

Returns the value of *EXPRESSION*.

Note that parentheses will usually require escaping with **\\** (so they appear as **\\(** and **\\)**) to avoid being interpreted as a command substitution.

# [EXAMPLES](#EXAMPLES)

If the **/tmp** directory exists, copy the **/etc/motd** file to it:

```
if test -d /tmp


    cp /etc/motd /tmp/motd
end
```

If the variable **MANPATH** is defined and not empty, print the contents. (If **MANPATH** is not defined, then it will expand to zero arguments, unless quoted.)

```
if test -n "$MANPATH"


    echo $MANPATH
end
```

Be careful with unquoted variables:

```
if test -n $MANPATH


    # This will also be reached if $MANPATH is unset,


    # because in that case we have `test -n`, so it checks if "-n" is non-empty, and it is.


    echo $MANPATH
end
```

This will change in a future release of fish, or already with the **test-require-arg** feature flag &lt;#featureflags&gt; - if $MANPATH is unset, **if test -n $MANPATH** will be false.

Parentheses and the **-o** and **-a** operators can be combined to produce more complicated expressions. In this example, success is printed if there is a **/foo** or **/bar** file as well as a **/baz** or **/bat** file.

```
if test \( -f /foo -o -f /bar \) -a \( -f /baz -o -f /bat \)


    echo Success.
end
```

Numerical comparisons will fail if one of the operands is not a number:

```
if test 42 -eq "The answer to life, the universe and everything"


    echo So long and thanks for all the fish # will not be executed
end
```

A common comparison is with **status** &lt;#envvar-status&gt;:

```
if test $status -eq 0


    echo "Previous command succeeded"
end
```

The previous test can likewise be inverted:

```
if test ! $status -eq 0


    echo "Previous command failed"
end
```

which is logically equivalent to the following:

```
if test $status -ne 0


    echo "Previous command failed"
end
```

# [STANDARDS](#STANDARDS)

Unlike many things in fish, **test** implements a subset of the IEEE Std 1003.1-2008 (POSIX.1) standard &lt;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html&gt;. The following exceptions apply:

- The **&lt;** and **&gt;** operators for comparing strings are not implemented.
- With **test-require-arg**, the zero- and one-argument modes will behave differently.

In cases such as this, one can use **command** **test** to explicitly use the system's standalone **test** rather than this **builtin** **test**.

# [SEE ALSO](#SEE_ALSO)

Other commands that may be useful as a condition, and are often easier to use:

- string - manipulate strings &lt;&gt;, which can do string operations including wildcard and regular expression matching
- path - manipulate and check paths &lt;&gt;, which can do file checks and operations, including filters on multiple paths at once

# [Copyright](#Copyright)

fish-shell developers

October 10, 2026 4.9

---

Document: test(1) from fish 4.9.3-r1 (Chainguard OS)
Source: https://github.com/fish-shell/fish-shell
