# Tests: [[ ]], [ ] and (( )) — Bash / Shell Scripting

Source: https://www.skillbyai.com/en/bash/c-tests

> Write string, numeric and file tests correctly.

## How Bash decides true or false

In Bash, `if` runs a **command** and branches on its **exit status**: 0 is true, non-zero is false. Test commands make conditions. **`[ ... ]`** (the `test` command) is POSIX and works in any shell, but needs careful quoting. **`[[ ... ]]`** is a Bash keyword that is safer and more capable: no word splitting of variables inside it, `&&` and `||` inside the brackets, pattern matching with `==` (`[[ $file == *.log ]]`) and regular expressions with `=~` (captures in `BASH_REMATCH`). **String tests**: `-z` (empty), `-n` (non-empty), `==`, `!=`. **Numeric tests** inside brackets use `-eq`, `-ne`, `-lt`, `-le`, `-gt`, `-ge`; `<` and `>` inside `[[ ]]` compare **strings** alphabetically, which is a common bug. For numbers, prefer **`(( ... ))`**, an arithmetic test where `(( count > 10 ))` works naturally. **File tests**: `-e` exists, `-f` regular file, `-d` directory, `-r`/`-w`/`-x` permissions, `-s` non-empty, `-L` symlink.

## Exit status drives every decision

if, while and && all branch on whether a command returned 0.

![A command box emitting either a green zero badge leading to a tick path or a red non-zero badge leading to a cross path.](assets/figures/bash/section-3-map.svg) — Figure 3.1 — Exit status as the basis of conditions.

## Common tests

Strings, numbers, files and patterns.

```bash
name="report.log"
count=12

if [[ -z "$name" ]]; then echo "empty name"; fi
if [[ $name == *.log ]]; then echo "a log file"; fi
if [[ $name =~ ^([a-z]+)\.log$ ]]; then echo "base: ${BASH_REMATCH[1]}"; fi

if (( count > 10 )); then echo "more than ten"; fi
if [[ $count -ge 10 && -f /etc/hosts ]]; then echo "both true"; fi

[[ "10" < "9" ]] && echo "string compare: '10' sorts before '9'"   # trap!

if [[ -d /var/log && -r /var/log/syslog ]]; then echo "can read syslog"; fi
```

## Use (( )) for numbers

`[[ 10 > 9 ]]` compares strings, and "10" sorts before "9", so the test is false. Write numeric comparisons as `(( a > b ))` or with `-gt` to avoid this trap.

**Quiz:** Which test correctly checks whether the number in $n is greater than 10?

- [x] (( n > 10 ))
- [ ] [[ $n > 10 ]]
- [ ] [ $n > 10 ]
- [ ] [[ $n == >10 ]]

*Answer:* (( n > 10 )). Arithmetic evaluation compares numbers; > inside [[ ]] compares strings and inside [ ] is a redirection.
