🛠️ ITBlogs.pro

Bash Scripting Cheat Sheet: Variables, Loops, Functions and More

Oct 16, 2026 · Bash · 4 min read

A practical reference for writing Bash scripts, from the first line to loops and functions. Everything here works in Bash 4+ (Ubuntu, Debian, RHEL and most others).

Your first script

#!/usr/bin/env bash
echo "Hello, $USER"
chmod +x hello.sh
./hello.sh

The first line (the shebang) tells the system which interpreter to use. #!/usr/bin/env bash finds Bash wherever it is installed.

Variables

name="Alice"            # NO spaces around =
echo "Hello, $name"     # double quotes expand variables
echo 'Hello, $name'     # single quotes do not
echo "${name}_backup"   # braces when text follows the name

today=$(date +%F)       # capture a command's output
count=$((2 + 3))        # arithmetic

Always quote variables: "$name". Unquoted values break on spaces and wildcards.

Defaults and checks:

echo "${PORT:-8080}"            # use 8080 if PORT is unset or empty
: "${API_KEY:?API_KEY is required}"   # abort with a message if missing

Arguments

echo "Script: $0"
echo "First arg: $1"
echo "All args: $@"       # use "$@" to preserve each argument
echo "Number of args: $#"
shift                     # drop $1, so $2 becomes $1

Conditions

if [[ -f "$file" ]]; then
    echo "file exists"
elif [[ -d "$file" ]]; then
    echo "it's a directory"
else
    echo "nothing there"
fi

Use [[ ... ]] in Bash instead of the old [ ... ]; it's safer with empty values and supports &&, ||, and pattern matching.

Test True when
-f f / -d f / -e f regular file / directory / exists
-r f / -w f / -x f readable / writable / executable
-s f file exists and isn't empty
-z "$s" / -n "$s" string is empty / not empty
"$a" == "$b" strings equal
"$s" == *.txt glob match
"$s" =~ ^[0-9]+$ regex match
$a -eq $b, -ne, -lt, -le, -gt, -ge numeric compare

Numbers are also easy with (( )):

if (( count > 10 )); then echo "many"; fi

case

case "$1" in
    start)   echo "starting" ;;
    stop)    echo "stopping" ;;
    restart|reload) echo "restarting" ;;
    *)       echo "Usage: $0 {start|stop|restart}" >&2; exit 1 ;;
esac

Loops

for i in 1 2 3; do echo "$i"; done
for i in {1..5}; do echo "$i"; done
for f in *.log; do gzip "$f"; done                 # quote "$f" in the command

while read -r line; do
    echo "Line: $line"
done < file.txt                                     # read a file line by line

until ping -c1 -W1 example.com >/dev/null; do sleep 2; done

Functions

log() {
    local level="$1"; shift          # local keeps variables inside the function
    echo "$(date '+%F %T') [$level] $*"
}

is_root() { [[ $EUID -eq 0 ]]; }

log INFO "starting"
is_root || { log ERROR "run as root"; exit 1; }

A function returns an exit status (0 = success). To return text, echo it and capture with $(...).

Exit codes and chaining

Every command returns a number: 0 means success, anything else means failure. $? holds the last one.

mkdir /tmp/x && echo "created"        # run second only if first succeeded
grep -q root /etc/passwd || echo "no" # run second only if first failed
cmd1; cmd2                            # run both regardless
exit 0

Arrays and strings

files=(a.txt b.txt "my file.txt")
echo "${files[0]}"          # first element
echo "${#files[@]}"         # length
for f in "${files[@]}"; do echo "$f"; done

path="/var/log/syslog.1"
echo "${path##*/}"          # syslog.1       (strip longest prefix up to /)
echo "${path%.*}"           # /var/log/syslog (strip shortest suffix .*)
echo "${path/log/LOG}"      # replace first match
echo "${#path}"             # length
echo "${path^^}"            # UPPERCASE

Redirects and pipes

cmd > out.txt          # overwrite stdout
cmd >> out.txt         # append
cmd 2> err.txt         # stderr only
cmd > all.txt 2>&1     # stdout and stderr together
cmd > /dev/null 2>&1   # discard everything
cmd1 | cmd2            # pipe stdout into the next command

Here-docs and input

cat > /etc/myapp.conf <<EOF
port=$PORT
EOF

read -r -p "Continue? [y/N] " answer
[[ "$answer" =~ ^[Yy]$ ]] || exit 0

Quote the delimiter (<<'EOF') to stop variable expansion inside the block.

Next step

Scripts run as a service or from cron deserve extra safety. See Write safer Bash scripts.