Skip to content
abdallahmekky

CHEAT SHEET

Bash cheat sheet

A Bash scripting quick reference, written for study. Copy any block with one click. Syntax is for Bash 4 and newer; current RHEL ships Bash 5.

All cheat sheets

Getting started

#!/usr/bin/env bash# hello.shecho "Hello, $USER"
chmod +x hello.sh   # make it executable./hello.sh          # run itbash hello.sh       # run it without chmod

The shebang goes on the very first line. #!/bin/bash works too on RHEL.

set -euo pipefail   # stop on errors, unset variables and failed pipes

Variables

name="Abdallah"                  # no spaces around =echo "$name"                     # always quote expansionsecho "${name}_file"              # braces separate the namereadonly PI=3.14                 # constantunset name                       # remove a variableexport PATH="$PATH:$HOME/bin"    # visible to child processestoday=$(date +%F)                # command substitutionn=$((3 + 4))                     # arithmetic

Special variables

VariableMeaning
$0Name of the script
$1 ... $9Positional arguments
$#Number of arguments
"$@"All arguments, each one kept as a separate word
"$*"All arguments joined into one single word
$?Exit status of the last command
$$PID of the current shell
$!PID of the last background job
$_Last argument of the previous command
$LINENOCurrent line number in the script
$RANDOMA random number from 0 to 32767
$SECONDSSeconds since the shell started
$EUIDEffective user ID (0 means root)

Parameter expansion

${var:-default}     # use default if var is unset or empty${var:=default}     # assign default if var is unset or empty${var:?message}     # print message and exit if unset or empty${var:+alt}         # alt if var is set and not empty${#var}             # length of the value${var:2:3}          # substring: offset 2, length 3${var#prefix}       # remove the shortest matching prefix${var##*/}          # remove the longest prefix (like basename)${var%suffix}       # remove the shortest matching suffix${var%%.*}          # remove the longest suffix${var/old/new}      # replace the first match${var//old/new}     # replace every match${var^^}            # UPPERCASE${var,,}            # lowercase

Strings and quoting

SyntaxMeaning
'text'Literal text, nothing is expanded
"text"Expands $variables, $(commands) and backslash escapes
$'a\tb'ANSI-C quoting: \t, \n and similar escapes work
\$Escape a single character
cat <<EOFHello $USEREOFcat <<'EOF'              # quoted marker: no expansion inside$HOME stays literalEOFgrep foo <<< "$text"     # here-string: feed a variable to stdin

Conditionals

if [[ -f "$file" ]]; then  echo "file exists"elif [[ -d "$file" ]]; then  echo "directory"else  echo "missing"fi[[ -z "$var" ]] && echo "empty"     # && runs when the test is true[[ -n "$var" ]] || echo "unset"     # || runs when the test is false
case "$1" in  start)  echo "starting" ;;  stop)   echo "stopping" ;;  *)      echo "usage: $0 {start|stop}" ;;esac

Test operators

TestTrue when
-e fileExists
-f fileIs a regular file
-d dirIs a directory
-L fileIs a symbolic link
-r -w -x fileReadable, writable, executable
-s fileExists and is not empty
-z strString is empty
-n strString is not empty
str == patternEqual (a glob pattern inside [[ ]])
str != patternNot equal
str =~ regexMatches an extended regular expression
a -eq bIntegers: equal
-ne -lt -le -gt -geIntegers: not equal, less, less or equal, greater, greater or equal
(( a < b ))Arithmetic comparison
! && ||Not, and, or

Loops

for i in 1 2 3; do echo "$i"; donefor i in {1..5}; do echo "$i"; done                # brace rangefor ((i = 0; i < 5; i++)); do echo "$i"; done      # C-stylefor f in *.log; do echo "$f"; done                 # glob (quote "$f" when you use it)while read -r line; do echo "$line"; done < file.txt    # file, line by linewhile true; do sleep 1; done                            # foreveruntil ping -c1 host &>/dev/null; do sleep 1; done       # repeat until it succeedsbreak      # leave the loopcontinue   # skip to the next round

Functions

greet() {  local name="${1:-world}"    # local keeps the variable inside the function  echo "Hello, $name"}greet "Abdallah"              # call itresult=$(greet "Abdallah")    # capture its output
is_root() {  [[ $EUID -eq 0 ]]           # the last command's status is the return value}if is_root; then echo "root"; fi

Arrays

arr=(one two "three four")echo "${arr[0]}"            # first elementecho "${arr[@]}"            # all elementsecho "${#arr[@]}"           # number of elementsecho "${!arr[@]}"           # indexesecho "${arr[@]:1:2}"        # slice: from index 1, two elementsarr+=(five)                 # appendunset 'arr[1]'              # delete one elementfor x in "${arr[@]}"; do echo "$x"; done     # loop safely

Associative arrays

Needs Bash 4 or newer.

declare -A portsports[http]=80ports[https]=443echo "${ports[http]}"       # one valueecho "${!ports[@]}"         # all keysecho "${ports[@]}"          # all valuesfor k in "${!ports[@]}"; do echo "$k=${ports[$k]}"; done

Redirection and pipes

cmd > file             # stdout to a file (overwrite)cmd >> file            # appendcmd 2> err.log         # stderr to a filecmd &> all.log         # stdout and stderr to a filecmd > out 2>&1         # same thing, portable formcmd < file             # a file as stdincmd1 | cmd2            # stdout of cmd1 into cmd2cmd |& tee run.log     # stdout and stderr, shown and savedcmd > /dev/null 2>&1   # throw all output awayecho "error" >&2       # write to stderr

Chaining and jobs

cmd1 && cmd2            # cmd2 only if cmd1 succeedscmd1 || cmd2            # cmd2 only if cmd1 failscmd1 ; cmd2             # run both, always(cd /tmp && ls)         # subshell: the cd does not leak out{ echo a; echo b; } > out    # group commands (note the spaces and ;)cmd &                   # run in the backgroundjobs                    # list background jobsfg %1                   # bring job 1 to the foregroundwait                    # wait for all background jobsnohup cmd &             # keep running after you log out

Expansions

echo $(date +%F)              # command substitutiondiff <(sort a) <(sort b)      # process substitutionecho {a,b,c}.txt              # a.txt b.txt c.txtecho file{1..3}               # file1 file2 file3echo ~                        # your home directoryecho *.txt                    # globecho "$((2 ** 8))"            # 256

Input and options

read -r -p "Name: " name          # prompt and read a lineread -r -s -p "Password: " pw     # silent inputwhile getopts "ab:" opt; do       # options: -a and -b value  case $opt in    a) echo "flag a" ;;    b) echo "b=$OPTARG" ;;    *) exit 1 ;;  esacdoneshift $((OPTIND - 1))             # drop the parsed options[[ $# -ge 1 ]] || { echo "usage: $0 file" >&2; exit 1; }

Errors and traps

set -euo pipefail                          # strict modetmp=$(mktemp)                              # temporary filetrap 'rm -f "$tmp"' EXIT                   # always clean uptrap 'echo "failed on line $LINENO" >&2' ERRcommand || true                            # allow this one to failif ! command; then echo "failed"; fi       # handle the failure yourselfexit 0                                     # leave with a status

Exit codes

CodeMeaning
0Success
1General error
2Misuse of a shell builtin or wrong usage
126Found, but not executable
127Command not found
130Stopped with Ctrl+C
128 + nKilled by signal n

Debugging

bash -n script.sh      # check the syntax onlybash -x script.sh      # trace every command as it runsset -x                 # turn tracing on inside a scriptset +x                 # turn it off againshellcheck script.sh   # lint it (ShellCheck is a separate tool)

Text one-liners

grep -rn "pattern" .              # search recursively, with line numberssed -i 's/old/new/g' file         # replace in placeawk '{print $1}' file             # first columncut -d: -f1 /etc/passwd           # first field, ":" delimitedsort file | uniq -c | sort -nr    # count duplicates, most common firsttr 'a-z' 'A-Z' < file             # uppercasewc -l file                        # count linestail -f /var/log/messages         # follow a log

Handy idioms

dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"    # folder of this script[[ $EUID -eq 0 ]] || { echo "run as root" >&2; exit 1; }      # require rootcommand -v git >/dev/null || { echo "git missing" >&2; exit 1; }   # require a commandmkdir -p "$dir/out"                                          # create it if missingwhile IFS=, read -r a b c; do echo "$a $b"; done < data.csv  # split CSV fieldsfind . -name "*.log" -print0 | xargs -0 rm                   # safe with spaces in names

More cheat sheets

Questions

How do I start a Bash script?

Put #!/usr/bin/env bash (or #!/bin/bash) on the first line, make the file executable with chmod +x script.sh, and run it with ./script.sh.

What does set -euo pipefail do?

-e stops the script when a command fails, -u treats unset variables as errors, and -o pipefail makes a pipeline fail if any command in it fails.

What is the difference between $@ and $*?

Quoted, "$@" expands to each argument as its own word, while "$*" joins all arguments into a single word. Use "$@" in almost every case.

How do I check if a file exists in Bash?

Use [[ -f "$file" ]] for a regular file, [[ -d "$dir" ]] for a directory, or [[ -e "$path" ]] for either one.

How do I run a command only if the previous one succeeded?

Use &&, for example mkdir build && cd build. Use || to run the second command only when the first one fails.