# FOREWORD
A collection of pure `bash` alternatives to external processes and programs. The `bash` scripting language is more powerful than people realise and most tasks can be accomplished without depending on external programs.
Calling an external process in `bash` is expensive and excessive use will cause a noticeable slowdown. Scripts and programs written using built-in methods (*where applicable*) will be faster, require fewer dependencies and afford a better understanding of the language itself.
The contents of this book provide a reference for solving problems encountered when writing programs and scripts in `bash`. Examples are in function formats showcasing how to incorporate these solutions into code.
<!-- CHAPTER END -->
# STRINGS
## Trim leading and trailing white-space from string
This is an alternative to `sed`, `awk`, `perl` and other tools. The
function below works by finding all leading and trailing white-space and
removing it from the start and end of the string. The `:` built-in is used in place of a temporary variable.
**Example Function:**
```sh
trim_string() {
# Usage: trim_string " example string "
: "${1#"${1%%[![:space:]]*}"}"
: "${_%"${_##*[![:space:]]}"}"
printf '%s\n' "$_"
}
```
**Example Usage:**
```shell
$ trim_string " Hello, World "
Hello, World
$ name=" John Black "
$ trim_string "$name"
John Black
```
## Trim all white-space from string and truncate spaces
This is an alternative to `sed`, `awk`, `perl` and other tools. The
function below works by abusing word splitting to create a new string
without leading/trailing white-space and with truncated spaces.
**Example Function:**
```sh
# shellcheck disable=SC2086,SC2048
trim_all() {
# Usage: trim_all " example string "
set -f
set -- $*
printf '%s\n' "$*"
set +f
}
```
**Example Usage:**
```shell
$ trim_all " Hello, World "
Hello, World
$ name=" John Black is my name. "
$ trim_all "$name"
John Black is my name.
```
## Use regex on a string
The result of `bash`'s regex matching can be used to replace `sed` for a
large number of use-cases.
**CAVEAT**: This is one of the few platform dependent `bash` features.
`bash` will use whatever regex engine is installed on the user's system.
Stick to POSIX regex features if aiming for compatibility.
**CAVEAT**: This example only prints the first matching group. When using
multiple capture groups some modification is needed.
**Example Function:**
```sh
regex() {
# Usage: regex "string" "regex"
[[ $1 =~ $2 ]] && printf '%s\n' "${BASH_REMATCH[1]}"
}
```
**Example Usage:**
```shell
$ # Trim leading white-space.
$ regex ' hello' '^\s*(.*)'
hello
$ # Validate a hex color.
$ regex "#FFFFFF" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$'
#FFFFFF
$ # Validate a hex color (invalid).
$ regex "red" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$'
# no output (invalid)
```
**Example Usage in script:**
```shell
is_hex_color() {
if [[ $1 =~ ^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$ ]]; then
printf '%s\n' "${BASH_REMATCH[1]}"
else
printf '%s\n' "error: $1 is an invalid color."
return 1
fi
}
read -r color
is_hex_color "$color" || color="#FFFFFF"
# Do stuff.
```
## Split a string on a delimiter
**CAVEAT:** Requires `bash` 4+
This is an alternative to `cut`, `awk` and other tools.
**Example Function:**
```sh
split() {
# Usage: split "string" "delimiter"
IFS=$'\n' read -d "" -ra arr <<< "${1//$2/$'\n'}"
printf '%s\n' "${arr[@]}"
}
```
**Example Usage:**
```shell
$ split "apples,oranges,pears,grapes" ","
apples
oranges
pears
grapes
$ split "1, 2, 3, 4, 5" ", "
1
2
3
4
5
# Multi char delimiters work too!
$ split "hello---world---my---name---is---john" "---"
hello
world
my
name
is
john
```
## Change a string to lowercase
**CAVEAT:** Requires `bash` 4+
**Example Function:**
```sh
lower() {
# Usage: lower "string"
printf '%s\n' "${1,,}"
}
```
**Example Usage:**
```shell
$ lower "HELLO"
hello
$ lower "HeLlO"
hello
$ lower "hello"
hello
```
## Change a string to uppercase
**CAVEAT:** Requires `bash` 4+
**Example Function:**
```sh
upper() {
# Usage: upper "string"
printf '%s\n' "${1^^}"
}
```
**Example Usage:**
```shell
$ upper "hello"
HELLO
$ upper "HeLlO"
HELLO
$ upper "HELLO"
HELLO
```
## Reverse a string case
**CAVEAT:** Requires `bash` 4+
**Example Function:**
```sh
reverse_case() {
# Usage: reverse_case "string"
printf '%s\n' "${1~~}"
}
```
**Example Usage:**
```shell
$ reverse_case "hello"
HELLO
$ reverse_case "HeLlO"
hElLo
$ reverse_case "HELLO"
hello
```
## Trim quotes from a string
**Example Function:**
```sh
trim_quotes() {
# Usage: trim_quotes "string"
: "${1//\'}"
printf '%s\n' "${_//\"}"
}
```
**Example Usage:**
```shell
$ var="'Hello', \"World\""
$ trim_quotes "$var"
Hello, World
```
## Strip all instances of pattern from string
**Example Function:**
```sh
strip_all() {
# Usage: strip_all "string" "pattern"
printf '%s\n' "${1//$2}"
}
```
**Example Usage:**
```shell
$ strip_all "The Quick Brown Fox" "[aeiou]"
Th Qck Brwn Fx
$ strip_all "The Quick Brown Fox" "[[:space:]]"
TheQuickBrownFox
$ strip_all "The Quick Brown Fox" "Quick "
The Brown Fox
```
## Strip first occurrence of pattern from string
**Example Function:**
```sh
strip() {
# Usage: strip "string" "pattern"
printf '%s\n' "${1/$2}"
}
```
**Example Usage:**
```shell
$ strip "The Quick Brown Fox" "[aeiou]"
Th Quick Brown Fox
$ strip "The Quick Brown Fox" "[[:space:]]"
TheQuick Brown Fox
```
## Strip pattern from start of string
**Example Function:**
```sh
lstrip() {
# Usage: lstrip "string" "pattern"
printf '%s\n' "${1##$2}"
}
```
**Example Usage:**
```shell
$ lstrip "The Quick Brown Fox" "The "
Quick Brown Fox
```
## Strip pattern from end of string
**Example Function:**
```sh
rstrip() {
# Usage: rstrip "string" "pattern"
printf '%s\n' "${1%%$2}"
}
```
**Example Usage:**
```shell
$ rstrip "The Quick Brown Fox" " Fox"
The Quick Brown
```
## Percent-encode a string
**Example Function:**
```sh
urlencode() {
# Usage: urlencode "string"
local LC_ALL=C
for (( i = 0; i < ${#1}; i++ )); do
: "${1:i:1}"
case "$_" in
[a-zA-Z0-9.~_-])
printf '%s' "$_"
;;
*)
printf '%%%02X' "'$_"
;;
esac
done
printf '\n'
}
```
**Example Usage:**
```shell
$ urlencode "https://github.com/dylanaraps/pure-bash-bible"
https%3A%2F%2Fgithub.com%2Fdylanaraps%2Fpure-bash-bible
```
## Decode a percent-encoded string
**Example Function:**
```sh
urldecode() {
# Usage: urldecode "string"
: "${1//+/ }"
printf '%b\n' "${_//%/\\x}"
}
```
**Example Usage:**
```shell
$ urldecode "https%3A%2F%2Fgithub.com%2Fdylanaraps%2Fpure-bash-bible"
https://github.com/dylanaraps/pure-bash-bible
```
## Check if string contains a sub-string
**Using a test:**
```shell
if [[ $var == *sub_string* ]]; then
printf '%s\n' "sub_string is in var."
fi
# Inverse (substring not in string).
if [[ $var != *sub_string* ]]; then
printf '%s\n' "sub_string is not in var."
fi
# This works for arrays too!
if [[ ${arr[*]} == *sub_string* ]]; then
printf '%s\n' "sub_string is in array."
fi
```
**Using a case statement:**
```shell
case "$var" in
*sub_string*)
# Do stuff
;;
*sub_string2*)
# Do more stuff
;;
*)
# Else
;;
esac
```
## Check if string starts with sub-string
```shell
if [[ $var == sub_string* ]]; then
printf '%s\n' "var starts with sub_string."
fi
# Inverse (var does not start with sub_string).
if [[ $var != sub_string* ]]; then
printf '%s\n' "var does not start with sub_string."
fi
```
## Check if string ends with sub-string
```shell
if [[ $var == *sub_string ]]; then
printf '%s\n' "var ends with sub_string."
fi
# Inverse (var does not end with sub_string).
if [[ $var != *sub_string ]]; then
printf '%s\n' "var does not end with sub_string."
fi
```
<!-- CHAPTER END -->
# ARRAYS
## Reverse an array
Enabling `extdebug` allows access to the `BASH_ARGV` array which stores
the current function’s arguments in reverse.
**CAVEAT**: Requires `shopt -s compat44` in `bash` 5.0+.
**Example Function:**
```sh
reverse_array() {
# Usage: reverse_array "array"
shopt -s extdebug
f()(printf '%s\n' "${BASH_ARGV[@]}"); f "$@"
shopt -u extdebug
}
```
**Example Usage:**
```shell
$ reverse_array 1 2 3 4 5
5
4
3
2
1
$ arr=(red blue green)
$ reverse_array "${arr[@]}"
green
blue
red
```
## Remove duplicate array elements
Create a temporary associative array. When setting associative array
values and a duplicate assignment occurs, bash overwrites the key. This
allows us to effectively remove array duplicates.
**CAVEAT:** Requires `bash` 4+
**CAVEAT:** List order may not stay the same.
**Example Function:**
```sh
remove_array_dups() {
# Usage: remove_array_dups "array"
declare -A tmp_array
for i in "$@"; do
[[ $i ]] && IFS=" " tmp_array["${i:- }"]=1
done
printf '%s\n' "${!tmp_array[@]}"
}
```
**Example Usage:**
```shell
$ remove_array_dups 1 1 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5
1
2
3
4
5
$ arr=(red red green blue blue)
$ remove_array_dups "${arr[@]}"
red
green
blue
```
## Random array element
**Example Function:**
```sh
random_array_element() {
# Usage: random_array_element "array"
local arr=("$@")
printf '%s\n' "${arr[RANDOM % $#]}"
}
```
**Example Usage:**
```shell
$ array=(red green blue yellow brown)
$ random_array_element "${array[@]}"
yellow
# Multiple arguments can also be passed.
$ random_array_element 1 2 3 4 5 6 7
3
```
## Cycle through an array
Each time the `printf` is called, the next array element is printed. When
the print hits the last array element it starts from the first element
again.
```sh
arr=(a b c d)
cycle() {
printf '%s ' "${arr[${i:=0}]}"
((i=i>=${#arr[@]}-1?0:++i))
}
```
## Toggle between two values
This works the same as above, this is just a different use case.
```sh
arr=(true false)
cycle() {
printf '%s ' "${arr[${i:=0}]}"
((i=i>=${#arr[@]}-1?0:++i))
}
```
<!-- CHAPTER END -->
# LOOPS
## Loop over a range of numbers
Alternative to `seq`.
```shell
# Loop from 0-100 (no variable support).
for i in {0..100}; do
printf '%s\n' "$i"
done
```
## Loop over a variable range of numbers
Alternative to `seq`.
```shell
# Loop from 0-VAR.
VAR=50
for ((i=0;i<=VAR;i++)); do
printf '%s\n' "$i"
done
```
## Loop over an array
```shell
arr=(apples oranges tomatoes)
# Just elements.
for element in "${arr[@]}"; do
printf '%s\n' "$element"
done
```
## Loop over an array with an index
```shell
arr=(apples oranges tomatoes)
# Elements and index.
for i in "${!arr[@]}"; do
printf '%s\n' "${arr[i]}"
done
# Alternative method.
for ((i=0;i<${#arr[@]};i++)); do
printf '%s\n' "${arr[i]}"
done
```
## Loop over the contents of a file
```shell
while read -r line; do
printf '%s\n' "$line"
done < "file"
```
## Loop over files and directories
Don’t use `ls`.
```shell
# Greedy example.
for file in *; do
printf '%s\n' "$file"
done
# PNG files in dir.
for file in ~/Pictures/*.png; do
printf '%s\n' "$file"
done
# Iterate over directories.
for dir in ~/Downloads/*/; do
printf '%s\n' "$dir"
done
# Brace Expansion.
for file in /path/to/parentdir/{file1,file2,subdir/file3}; do
printf '%s\n' "$file"
done
# Iterate recursively.
shopt -s globstar
for file in ~/Pictures/**/*; do
printf '%s\n' "$file"
done
shopt -u globstar
```
<!-- CHAPTER END -->
# FILE HANDLING
**CAVEAT:** `bash` does not handle binary data properly in versions `< 4.4`.
## Read a file to a string
Alternative to the `cat` command.
```shell
file_data="$(<"file")"
```
## Read a file to an array (*by line*)
Alternative to the `cat` command.
```shell
# Bash <4 (discarding empty lines).
IFS=$'\n' read -d "" -ra file_data < "file"
# Bash <4 (preserving empty lines).
while read -r line; do
file_data+=("$line")
done < "file"
# Bash 4+
mapfile -t file_data < "file"
```
## Get the first N lines of a file
Alternative to the `head` command.
**CAVEAT:** Requires `bash` 4+
**Example Function:**
```sh
head() {
# Usage: head "n" "file"
mapfile -tn "$1" line < "$2"
printf '%s\n' "${line[@]}"
}
```
**Example Usage:**
```shell
$ head 2 ~/.bashrc
# Prompt
PS1='➜ '
$ head 1 ~/.bashrc
# Prompt
```
## Get the last N lines of a file
Alternative to the `tail` command.
**CAVEAT:** Requires `bash` 4+
**Example Function:**
```sh
tail() {
# Usage: tail "n" "file"
mapfile -tn 0 line < "$2"
printf '%s\n' "${line[@]: -$1}"
}
```
**Example Usage:**
```shell
$ tail 2 ~/.bashrc
# Enable tmux.
# [[ -z "$TMUX" ]] && exec tmux
$ tail 1 ~/.bashrc
# [[ -z "$TMUX" ]] && exec tmux
```
## Get the number of lines in a file
Alternative to `wc -l`.
**Example Function (bash 4):**
```sh
lines() {
# Usage: lines "file"
mapfile -tn 0 lines < "$1"
printf '%s\n' "${#lines[@]}"
}
```
**Example Function (bash 3):**
This method uses less memory than the `mapfile` method and works in `bash` 3 but it is slower for bigger files.
```sh
lines_loop() {
# Usage: lines_loop "file"
count=0
while IFS= read -r _; do
((count++))
done < "$1"
printf '%s\n' "$count"
}
```
**Example Usage:**
```shell
$ lines ~/.bashrc
48
$ lines_loop ~/.bashrc
48
```
## Count files or directories in directory
This works by passing the output of the glob to the function and then counting the number of arguments.
**Example Function:**
```sh
count() {
# Usage: count /path/to/dir/*
# count /path/to/dir/*/
printf '%s\n' "$#"
}
```
**Example Usage:**
```shell
# Count all files in dir.
$ count ~/Downloads/*
232
# Count all dirs in dir.
$ count ~/Downloads/*/
45
# Count all jpg files in dir.
$ count ~/Pictures/*.jpg
64
```
## Create an empty file
Alternative to `touch`.
```shell
# Shortest.
>file
# Longer alternatives:
:>file
echo -n >file
printf '' >file
```
## Extract lines between two markers
**Example Function:**
```sh
extract() {
# Usage: extract file "opening marker" "closing marker"
while IFS=$'\n' read -r line; do
[[ $extract && $line != "$3" ]] &&
printf '%s\n' "$line"
[[ $line == "$2" ]] && extract=1
[[ $line == "$3" ]] && extract=
done < "$1"
}
```
**Example Usage:**
```shell
# Extract code blocks from MarkDown file.
$ extract ~/projects/pure-bash/README.md '```sh' '```'
# Output here...
```
<!-- CHAPTER END -->
# FILE PATHS
## Get the directory name of a file path
Alternative to the `dirname` command.
**Example Function:**
```sh
dirname() {
# Usage: dirname "path"
local tmp=${1:-.}
[[ $tmp != *[!/]* ]] && {
printf '/\n'
return
}
tmp=${tmp%%"${tmp##*[!/]}"}
[[ $tmp != */* ]] && {
printf '.\n'
return
}
tmp=${tmp%/*}
tmp=${tmp%%"${tmp##*[!/]}"}
printf '%s\n' "${tmp:-/}"
}
```
**Example Usage:**
```shell
$ dirname ~/Pictures/Wallpapers/1.jpg
/home/black/Pictures/Wallpapers
$ dirname ~/Pictures/Downloads/
/home/black/Pictures
```
## Get the base-name of a file path
Alternative to the `basename` command.
**Example Function:**
```sh
basename() {
# Usage: basename "path" ["suffix"]
local tmp
tmp=${1%"${1##*[!/]}"}
tmp=${tmp##*/}
tmp=${tmp%"${2/"$tmp"}"}
printf '%s\n' "${tmp:-/}"
}
```
**Example Usage:**
```shell
$ basename ~/Pictures/Wallpapers/1.jpg
1.jpg
$ basename ~/Pictures/Wallpapers/1.jpg .jpg
1
$ basename ~/Pictures/Downloads/
Downloads
```
<!-- CHAPTER END -->
# VARIABLES
## Assign and access a variable using a variable
```shell
$ hello_world="value"
# Create the variable name.
$ var="world"
$ ref="hello_$var"
# Print the value of the variable name stored in 'hello_$var'.
$ printf '%s\n' "${!ref}"
value
```
Alternatively, on `bash` 4.3+:
```shell
$ hello_world="value"
$ var="world"
# Declare a nameref.
$ declare -n ref=hello_$var
$ printf '%s\n' "$ref"
value
```
## Name a variable based on another variable
```shell
$ var="world"
$ declare "hello_$var=value"
$ printf '%s\n' "$hello_world"
value
```
<!-- CHAPTER END -->
# ESCAPE SEQUENCES
Contrary to popular belief, there is no issue in utilizing raw escape sequences. Using `tput` abstracts the same ANSI sequences as if printed manually. Worse still, `tput` is not actually portable. There are a number of `tput` variants each with different commands and syntaxes (*try `tput setaf 3` on a FreeBSD system*). Raw sequences are fine.
## Text Colors
**NOTE:** Sequences requiring RGB values only work in True-Color Terminal Emulators.
| Sequence | What does it do? | Value |
| -------- | ---------------- | ----- |
| `\e[38;5;<NUM>m` | Set text foreground color. | `0-255`
| `\e[48;5;<NUM>m` | Set text background color. | `0-255`
| `\e[38;2;<R>;<G>;<B>m` | Set text foreground color to RGB color. | `R`, `G`, `B`
| `\e[48;2;<R>;<G>;<B>m` | Set text background color to RGB color. | `R`, `G`, `B`
## Text Attributes
**NOTE:** Prepend 2 to any code below to turn it's effect off
(examples: 21=bold text off, 22=faint text off, 23=italic text off).
| Sequence | What does it do? |
| -------- | ---------------- |
| `\e[m` | Reset text formatting and colors. |
| `\e[1m` | Bold text. |
| `\e[2m` | Faint text. |
| `\e[3m` | Italic text. |
| `\e[4m` | Underline text. |
| `\e[5m` | Blinking text. |
| `\e[7m` | Highlighted text. |
| `\e[8m` | Hidden text. |
| `\e[9m` | Strike-through text. |
## Cursor Movement
| Sequence | What does it do? | Value |
| -------- | ---------------- | ----- |
| `\e[<LINE>;<COLUMN>H` | Move cursor to absolute position. | `line`, `column`
| `\e[H` | Move cursor to home position (`0,0`). |
| `\e[<NUM>A` | Move cursor up N lines. | `num`
| `\e[<NUM>B` | Move cursor down N lines. | `num`
| `\e[<NUM>C` | Move cursor right N columns. | `num`
| `\e[<NUM>D` | Move cursor left N columns. | `num`
| `\e[s` | Save cursor position. |
| `\e[u` | Restore cursor position. |
## Erasing Text
| Sequence | What does it do? |
| -------- | ---------------- |
| `\e[K` | Erase from cursor position to end of line.
| `\e[1K` | Erase from cursor position to start of line.
| `\e[2K` | Erase the entire current line.
| `\e[J` | Erase from the current line to the bottom of the screen.
| `\e[1J` | Erase from the current line to the top of the screen.
| `\e[2J` | Clear the screen.
| `\e[2J\e[H` | Clear the screen and move cursor to `0,0`.
<!-- CHAPTER END -->
# PARAMETER EXPANSION
## Indirection
| Parameter | What does it do? |
| --------- | ---------------- |
| `${!VAR}` | Access a variable based on the value of `VAR`.
| `${!VAR*}` | Expand to `IFS` separated list of variable names starting with `VAR`. |
| `${!VAR@}` | Expand to `IFS` separated list of variable names starting with `VAR`. If double-quoted, each variable name expands to a separate word. |
## Replacement
| Parameter | What does it do? |
| --------- | ---------------- |
| `${VAR#PATTERN}` | Remove shortest match of pattern from start of string. |
| `${VAR##PATTERN}` | Remove longest match of pattern from start of string. |
| `${VAR%PATTERN}` | Remove shortest match of pattern from end of string. |
| `${VAR%%PATTERN}` | Remove longest match of pattern from end of string. |
| `${VAR/PATTERN/REPLACE}` | Replace first match with string.
| `${VAR//PATTERN/REPLACE}` | Replace all matches with string.
| `${VAR/PATTERN}` | Remove first match.
| `${VAR//PATTERN}` | Remove all matches.
## Length
| Parameter | What does it do? |
| --------- | ---------------- |
| `${#VAR}` | Length of var in characters.
| `${#ARR[@]}` | Length of array in elements.
## Expansion
| Parameter | What does it do? |
| --------- | ---------------- |
| `${VAR:OFFSET}` | Remove first `N` chars from variable.
| `${VAR:OFFSET:LENGTH}` | Get substring from `N` character to `N` character. <br> (`${VAR:10:10}`: Get sub-string from char `10` to char `20`)
| `${VAR:: OFFSET}` | Get first `N` chars from variable.
| `${VAR:: -OFFSET}` | Remove last `N` chars from variable.
| `${VAR: -OFFSET}` | Get last `N` chars from variable.
| `${VAR:OFFSET:-OFFSET}` | Cut first `N` chars and last `N` chars. | `bash 4.2+` |
## Case Modification
| Parameter | What does it do? | CAVEAT |
| --------- | ---------------- | ------ |
| `${VAR^}` | Uppercase first character. | `bash 4+` |
| `${VAR^^}` | Uppercase all characters. | `bash 4+` |
| `${VAR,}` | Lowercase first character. | `bash 4+` |
| `${VAR,,}` | Lowercase all characters. | `bash 4+` |
| `${VAR~}` | Reverse case of first character. | `bash 4+` |
| `${VAR~~}` | Reverse case of all characters. | `bash 4+` |
## Default Value
| Parameter | What does it do? |
| --------- | ---------------- |
| `${VAR:-STRING}` | If `VAR` is empty or unset, use `STRING` as its value.
| `${VAR-STRING}` | If `VAR` is unset, use `STRING` as its value.
| `${VAR:=STRING}` | If `VAR` is empty or unset, set the value of `VAR` to `STRING`.
| `${VAR=STRING}` | If `VAR` is unset, set the value of `VAR` to `STRING`.
| `${VAR:+STRING}` | If `VAR` is not empty, use `STRING` as its value.
| `${VAR+STRING}` | If `VAR` is set, use `STRING` as its value.
| `${VAR:?STRING}` | Display an error if empty or unset.
| `${VAR?STRING}` | Display an error if unset.
<!-- CHAPTER END -->
# BRACE EXPANSION
## Ranges
```shell
# Syntax: {<START>..<END>}
# Print numbers 1-100.
echo {1..100}
# Print range of floats.
echo 1.{1..9}
# Print chars a-z.
echo {a..z}
echo {A..Z}
# Nesting.
echo {A..Z}{0..9}
# Print zero-padded numbers.
# CAVEAT: bash 4+
echo {01..100}
# Change increment amount.
# Syntax: {<START>..<END>..<INCREMENT>}
# CAVEAT: bash 4+
echo {1..10..2} # Increment by 2.
```
## String Lists
```shell
echo {apples,oranges,pears,grapes}
# Example Usage:
# Remove dirs Movies, Music and ISOS from ~/Downloads/.
rm -rf ~/Downloads/{Movies,Music,ISOS}
```
<!-- CHAPTER END -->
# CONDITIONAL EXPRESSIONS
## File Conditionals
| Expression | Value | What does it do? |
| ---------- | ------ | ---------------- |
| `-a` | `file` | If file exists.
| `-b` | `file` | If file exists and is a block special file.
| `-c` | `file` | If file exists and is a character special file.
| `-d` | `file` | If file exists and is a directory.
| `-e` | `file` | If file exists.
| `-f` | `file` | If file exists and is a regular file.
| `-g` | `file` | If file exists and its set-group-id bit is set.
| `-h` | `file` | If file exists and is a symbolic link.
| `-k` | `file` | If file exists and its sticky-bit is set
| `-p` | `file` | If file exists and is a named pipe (*FIFO*).
| `-r` | `file` | If file exists and is readable.
| `-s` | `file` | If file exists and its size is greater than zero.
| `-t` | `fd` | If file descriptor is open and refers to a terminal.
| `-u` | `file` | If file exists and its set-user-id bit is set.
| `-w` | `file` | If file exists and is writable.
| `-x` | `file` | If file exists and is executable.
| `-G` | `file` | If file exists and is owned by the effective group ID.
| `-L` | `file` | If file exists and is a symbolic link.
| `-N` | `file` | If file exists and has been modified since last read.
| `-O` | `file` | If file exists and is owned by the effective user ID.
| `-S` | `file` | If file exists and is a socket.
## File Comparisons
| Expression | What does it do? |
| ---------- | ---------------- |
| `file -ef file2` | If both files refer to the same inode and device numbers.
| `file -nt file2` | If `file` is newer than `file2` (*uses modification time*) or `file` exists and `file2` does not.
| `file -ot file2` | If `file` is older than `file2` (*uses modification time*) or `file2` exists and `file` does not.
## Variable Conditionals
| Expression | Value | What does it do? |
| ---------- | ----- | ---------------- |
| `-o` | `opt` | If shell option is enabled.
| `-v` | `var` | If variable has a value assigned.
| `-R` | `var` | If variable is a name reference.
| `-z` | `var` | If the length of string is zero.
| `-n` | `var` | If the length of string is non-zero.
## Variable Comparisons
| Expression | What does it do? |
| ---------- | ---------------- |
| `var = var2` | Equal to.
| `var == var2` | Equal to (*synonym for `=`*).
| `var != var2` | Not equal to.
| `var < var2` | Less than (*in ASCII alphabetical order.*)
| `var > var2` | Greater than (*in ASCII alphabetical order.*)
<!-- CHAPTER END -->