opscape

Test operators cheat sheet

Every file, string, number and variable test for bash's [ ] and [[ ]], plus pattern and regex matching, with real output.

[ ] (also spelled test) is a POSIX command: quote every variable in it. [[ ]] is bash syntax: no word splitting inside, &&/|| work, == matches globs and =~ regular expressions. A test exits 0 for true, 1 for false and 2 for a malformed expression. Each row prints yes or no so the result is visible.

Files

[[ -e notes.txt ]] && echo yes || echo no
The path exists (any type). False for a symlink whose target is missing.yes
[[ -f data/users.csv ]] && echo yes || echo no
Is a regular file (follows symlinks).yes
[[ -d data ]] && echo yes || echo no
Is a directory.yes
[[ -L link-to-notes ]] && echo yes || echo no
Is a symbolic link (-h is the same). Does not follow the link.yes
[[ -r notes.txt ]] && echo yes || echo no
Is readable by you. -w writable, -x executable (or searchable for a directory).yes
[[ -x scripts/greet.sh ]] && echo yes || echo no
Has execute permission for you.yes
touch empty; [[ -s empty ]] && echo yes || echo no
Exists and is larger than zero bytes.no
touch new; [[ new -nt notes.txt ]] && echo yes || echo no
First file is newer than the second (modification time). -ot is older.yes
[[ notes.txt -ef link-to-notes ]] && echo yes || echo no
Both names refer to the same file (same device and inode).yes
[[ -p /dev/stdin ]]
Is a named pipe. -S socket, -b block device, -c character device.
[[ -t 1 ]]
File descriptor 1 is a terminal; use it to decide whether to print colors.

Strings

v=""; [[ -z $v ]] && echo yes || echo no
The string is empty.yes
v=x; [[ -n $v ]] && echo yes || echo no
The string is not empty.yes
[[ abc == abc ]] && echo yes || echo no
Strings are equal. In [ ], the portable spelling is a single =.yes
[[ abc != abd ]] && echo yes || echo no
Strings differ.yes
[[ apple < banana ]] && echo yes || echo no
Sorts before, in the current locale. In [ ] write \<, or < becomes a redirection.yes
[[ 10 < 9 ]] && echo yes || echo no
< compares strings, so "10" sorts before "9". Use -lt or (( )) for numbers.yes

Patterns and regular expressions ([[ ]] only)

f=app.tar.gz; [[ $f == *.tar.gz ]] && echo yes || echo no
Glob pattern match when the right side is unquoted.yes
f=app.tar.gz; [[ $f == "*.tar.gz" ]] && echo yes || echo no
Quoting the right side compares literally.no
[[ v2.4.1 =~ ^v([0-9]+)\.([0-9]+) ]] && echo "major ${BASH_REMATCH[1]} minor ${BASH_REMATCH[2]}"
Extended regex match; capture groups land in BASH_REMATCH.major 2 minor 4
re="^[0-9]+$"; [[ 2026 =~ $re ]] && echo yes || echo no
Keep the regex in a variable and leave it unquoted, to avoid quoting surprises.yes

Integers

[[ 5 -eq 5 ]] && echo yes || echo no
Equal. -ne not equal.yes
[[ 3 -lt 10 ]] && echo yes || echo no
Less than. -le less or equal.yes
[[ 10 -gt 3 ]] && echo yes || echo no
Greater than. -ge greater or equal.yes
(( 10 > 3 )) && echo yes || echo no
The bash arithmetic form, with ordinary math symbols.yes
[ abc -gt 5 ]
A non-number in [ ] is an error (status 2), not false./bin/bash: line 1: [: abc: integer expression expected

Variables and options

v=""; [[ -v v ]] && echo yes || echo no
The variable is set, even if empty (bash 4.2+). Pass the name, not $v.yes
unset v; [[ -v v ]] && echo yes || echo no
False for an unset variable.no
set -u; [[ -o nounset ]] && echo yes || echo no
A shell option is on (set -o lists the names).yes

Combining

[[ -f notes.txt && -s notes.txt ]] && echo yes || echo no
Logical AND inside [[ ]]. || is OR.yes
[[ ! -d notes.txt ]] && echo yes || echo no
Negate a test.yes
f=a.csv; [[ -n $f && ( $f == *.csv || $f == *.tsv ) ]] && echo yes || echo no
Group with parentheses inside [[ ]], no escaping needed.yes
[ -f notes.txt ] && [ -r notes.txt ] && echo yes || echo no
Portable form for [ ] and sh; avoid the obsolescent -a and -o.yes