Bash Scripting
Every essential Bash scripting pattern: variables, conditionals, loops, functions, file operations, and error handling, with syntax and real use cases.93 commands · 6 sections
Bash scripts automate everything from deploys to cron jobs. This cheatsheet covers the patterns you write daily: variables and substitutions, conditionals, loops, functions, file operations, and the error-handling habits that keep scripts safe.
Every entry shows real syntax followed by the use case: when and why you reach for it.
Variables & Substitutions22
name="Jane"echo "$name"echo '$name'export PATH="$PATH:/new/bin"$(command)`command`${name:-default}${name:=default}${name:?error msg}${#name}${name%pattern} / ${name#pattern}${name//old/new}${name/old/new}$((${a:-0} + ${b:-0}))((i++))local var="x"declare -a arr=(a b c)${arr[@]}${#arr[@]}arr+=("new")read -r varIFS="," read -ra parts <<< "a,b,c"Conditionals17
if [[ $x -eq 5 ]]; then ...; fiif [[ "$a" == "yes" ]]; then ...; fiif [[ -f "$file" ]]; then ...; fiif [[ -d "$dir" ]]; then ...; fiif [[ -z "$var" ]]; then ...; fiif [[ -n "$var" ]]; then ...; fiif [[ -x "$file" ]]; then ...; fiif [[ $x -gt 10 && $x -lt 20 ]]; then ...; fiif [[ $x == "a" || $x == "b" ]]; then ...; fiif [[ "$a" =~ ^[0-9]+$ ]]; then ...; fiif cmd; then ...; fiif cmd; then ...; else ...; fiif ...; then ...; elif ...; then ...; ficase "$x" in
a) ... ;;
*) ... ;;
esac[ "$x" = "y" ]! commandcommand && echo ok || echo failLoops11
for f in *.txt; do ...; donefor i in {1..10}; do ...; donefor i in $(seq 1 10); do ...; donefor item in "${arr[@]}"; do ...; donefor ((i=0; i<10; i++)); do ...; donewhile read -r line; do ...; done < filewhile read -r line; do ...; done <<< "$var"until condition; do ...; donewhile true; do ...; donebreak / continuefor f in $(find . -name "*.js"); do ...; doneFunctions8
myfunc() {
echo "hi"
}myfunc arg1 arg2$1 $2 ${@}return 1result=$(myfunc)function name() { ... }trap cleanup EXITexport -f myfuncFile Operations17
> file>> file2> /dev/null2>&1cmd | tee filecmd1 | cmd2cat file1 file2 > mergedgrep -q pattern file && actionfind . -name "*.tmp" -exec rm {} \;xargs -n1 cmddiff -u a.txt b.txtcp -a src/ dst/mv file file.bakmkdir -p a/b && cd a/bbasename /path/file.txtdirname /path/file.txtwc -l < fileError Handling & Debugging18
set -eset -uset -euo pipefailset -xbash -x script.shcmd || exit 1cmd || { echo "msg" >&2; exit 1; }cmd || handle_errorcmd &wait$?trap 'echo "killed"; exit 1' INT TERMtrap - ERR#!/usr/bin/env bashexit 0 / exit 1>&2 echo "error"command -v nodeset -o pipefailBash Scripting
Every essential Bash scripting pattern: variables, conditionals, loops, functions, file operations, and error handling, with syntax and real use cases.
Bash scripts automate everything from deploys to cron jobs. This cheatsheet covers the patterns you write daily: variables and substitutions, conditionals, loops, functions, file operations, and the error-handling habits that keep scripts safe.
Every entry shows real syntax followed by the use case: when and why you reach for it.
Variables & Substitutions
name="Jane": Assign a variable: NO spaces around the equals sign.echo "$name": Use the value: always double-quote variables.echo '$name': Single quotes: literal text, NO expansion.export PATH="$PATH:/new/bin": Export: make the variable available to child processes.$(command): Command substitution: capture output.`command`: Legacy command substitution: avoid; use $().${name:-default}: Default value if unset or empty.${name:=default}: Assign a default if unset: same as :- but writes the variable.${name:?error msg}: Fail loudly if unset: required parameters.${#name}: Length of the value.${name%pattern} / ${name#pattern}: Strip a suffix / prefix from a variable.${name//old/new}: Replace ALL occurrences in a variable.${name/old/new}: Replace the FIRST occurrence.$((${a:-0} + ${b:-0})): Arithmetic: always default numeric vars to avoid errors.((i++)): Arithmetic as a statement: counters in loops.local var="x": Function-local variable: never leak globals.declare -a arr=(a b c): Declare an array.${arr[@]}: ALL array elements: expand each as a separate word.${#arr[@]}: Array length.arr+=("new"): Append to an array.read -r var: Read user input into a variable.IFS="," read -ra parts <<< "a,b,c": Split a string into an array by delimiter.Conditionals
if [[ $x -eq 5 ]]; then ...; fi: Numeric comparison: ALWAYS double brackets.if [[ "$a" == "yes" ]]; then ...; fi: String comparison: quote the variables.if [[ -f "$file" ]]; then ...; fi: File exists and is a regular file.if [[ -d "$dir" ]]; then ...; fi: Directory exists.if [[ -z "$var" ]]; then ...; fi: Variable is EMPTY.if [[ -n "$var" ]]; then ...; fi: Variable is NOT empty.if [[ -x "$file" ]]; then ...; fi: File is executable.if [[ $x -gt 10 && $x -lt 20 ]]; then ...; fi: AND: both conditions.if [[ $x == "a" || $x == "b" ]]; then ...; fi: OR: either condition.if [[ "$a" =~ ^[0-9]+$ ]]; then ...; fi: Regex match: validate input.if cmd; then ...; fi: Test a COMMAND's exit status directly.if cmd; then ...; else ...; fi: The full if/else form.if ...; then ...; elif ...; then ...; fi: Multiple branches.case "$x" in
a) ... ;;
*) ... ;;
esac: Switch statement: cleaner than long if/elif chains.[ "$x" = "y" ]: Single brackets: POSIX-compatible but error-prone; prefer [[ ]].! command: Negate a command's success.command && echo ok || echo fail: One-line conditional: chain on success/failure.Loops
for f in *.txt; do ...; done: Iterate over glob matches: files.for i in {1..10}; do ...; done: Fixed range loop: brace expansion.for i in $(seq 1 10); do ...; done: Range loop with seq: dynamic bounds.for item in "${arr[@]}"; do ...; done: Iterate over an array: quote expansion!for ((i=0; i<10; i++)); do ...; done: C-style loop: index math.while read -r line; do ...; done < file: Read a file line by line: the safe line loop.while read -r line; do ...; done <<< "$var": Loop over a string's lines.until condition; do ...; done: Loop UNTIL the condition is true: wait patterns.while true; do ...; done: Infinite loop: with explicit break.break / continue: Exit the loop / skip an iteration.for f in $(find . -name "*.js"); do ...; done: Loop over command output: careful with spaces.Functions
myfunc() {
echo "hi"
}: Define a function.myfunc arg1 arg2: Call with arguments: no parentheses.$1 $2 ${@}: Arguments inside the function: $@ is all of them.return 1: Exit the function with a status: 0 success, nonzero fail.result=$(myfunc): Capture a function's stdout as a value.function name() { ... }: Alternative syntax: works but redundant.trap cleanup EXIT: Run a function when the script exits: guaranteed cleanup.export -f myfunc: Export a function to subshells: with bash -c and parallel.File Operations
> file: Redirect stdout to a file (overwrite).>> file: Append stdout to a file.2> /dev/null: Discard errors: silence expected failures.2>&1: Merge stderr into stdout: one log.cmd | tee file: Pipe to a file AND keep the terminal output.cmd1 | cmd2: Pipe: feed one command's output into the next.cat file1 file2 > merged: Concatenate into one file.grep -q pattern file && action: Conditional on a search: the if-command idiom.find . -name "*.tmp" -exec rm {} \;: Find and act on matches: {} is the filename.xargs -n1 cmd: Run a command once per input line: parallel with -P.diff -u a.txt b.txt: Unified diff: patch-style output.cp -a src/ dst/: Copy recursively preserving attributes: deploy pattern.mv file file.bak: Backup a file inline.mkdir -p a/b && cd a/b: Create and enter in one move.basename /path/file.txt: Filename without the directory.dirname /path/file.txt: Directory without the filename.wc -l < file: Count lines: clean output without the filename.Error Handling & Debugging
set -e: Exit on ANY command failure: the #1 safety habit.set -u: Error on undefined variables: catch typos.set -euo pipefail: The holy trinity: exit on error, undefined vars, and failed pipes.set -x: Trace: print every command before running. The debugger.bash -x script.sh: Run a script with tracing: no code edits.cmd || exit 1: Fail explicitly with a status.cmd || { echo "msg" >&2; exit 1; }: Fail with a message to stderr.cmd || handle_error: Run a recovery function on failure.cmd &: Background a command: get the prompt back.wait: Wait for ALL background jobs to finish.$?: Exit status of the LAST command.trap 'echo "killed"; exit 1' INT TERM: Handle signals: graceful Ctrl+C.trap - ERR: Run on ANY error: error reporting.#!/usr/bin/env bash: Shebang: make the script self-executing.exit 0 / exit 1: Exit with explicit status: 0 success, 1 failure, 2 usage.>&2 echo "error": Print to stderr: keep errors separate from output.command -v node: Check a tool is installed: before using it.set -o pipefail: Pipe fails if ANY stage fails: not just the last.Frequently asked questions
What is the difference between single and double quotes in Bash?
Single quotes preserve every character literally: no expansion happens. Double quotes allow variable expansion and command substitution, so "$name" prints the value while '$name' would print the literal text.
How do I write an if statement in Bash?
Use if [[ condition ]]; then ... elif ...; then ... else ...; fi. Double brackets [[ ]] are safer than single [ ] and support regex with =~, pattern matching, and string comparison operators.
How do I loop over files?
Use for f in *.txt; do echo "$f"; done to iterate over glob matches. For line-based input use while IFS= read -r line; do ...; done < file, which preserves spaces and handles missing trailing newlines.
How do I debug a Bash script?
Run bash -x script.sh to trace every command as it executes, or add set -x inside the script and set +x to disable. Use set -e to exit on errors and set -u to error on undefined variables.