☀️good morning

$ cheat sheet

Bash
Scripting

Everything you reach for. Copy, paste, ship.

#!

Shebang & basics

2 snippets
minimal shebang
#!/usr/bin/env bash
set -euo pipefail   # exit on error, unset var, pipe fail
Always the first line. Tells the OS which interpreter to use.
run & debug
chmod +x script.sh
bash -x script.sh   # trace mode, prints each line before running
Make executable and trace every command.
$

Variables

3 snippets
declare & use
name="Mayuresh"
age=28
echo "Hello, $name"
echo "Age: ${age}"        # braces = safe practice
No spaces around = when assigning.
readonly & unset
readonly API_KEY="abc123"
unset age
default values
echo ${NAME:-"guest"}       # fallback if unset
echo ${COUNT:=0}          # fallback + assign
echo ${MUST:?"required"}  # error if unset
Use :- to fallback, := to also assign, :? to error if unset.
${}

Parameter expansion

5 snippets
substring & length
str="hello world"
echo ${#str}          # length → 11
echo ${str:6}         # from index 6 → world
echo ${str:0:5}       # first 5 chars → hello
echo ${str: -5}       # last 5 chars → world
find & replace
str="foo foo foo"
echo ${str/foo/bar}    # first match → bar foo foo
echo ${str//foo/bar}   # all matches → bar bar bar
echo ${str/#foo/bar}   # only at start
echo ${str/%foo/bar}   # only at end
strip prefix / suffix
path="/usr/local/bin/script.sh"
echo ${path##*/}     # strip longest prefix → script.sh
echo ${path%/*}      # strip shortest suffix → /usr/local/bin
echo ${path%%.*}     # strip from first dot
echo ${path#*/}      # strip shortest prefix → usr/local/bin/script.sh
Single # or % = shortest match. Double = longest (greedy).
case conversion
str="Hello World"
echo ${str,,}        # all lowercase → hello world
echo ${str^^}        # all uppercase → HELLO WORLD
echo ${str,}         # first char lowercase
echo ${str^}         # first char uppercase
indirect expansion
var="PATH"
echo ${!var}         # expands to value of $PATH

# list all vars matching a prefix
echo ${!AWS_*}       # → AWS_ACCESS_KEY_ID AWS_SECRET ...
Use !varname to expand the value of the variable named by varname.
""

Strings & quotes

3 snippets
single vs double
echo 'Hello $USER'    # → Hello $USER
echo "Hello $USER"    # → Hello mayuresh
Single quotes = literal. Double quotes = expand variables and escapes.
heredoc
cat << EOF
line one
line two
EOF

# indented heredoc (bash 4+)
cat <<- EOF
	indented content
EOF
Great for multiline strings or passing text to commands.
string test patterns
[[ "$str" == *.log ]]     # glob match
[[ "$str" =~ ^[0-9]+$ ]]  # regex match
[[ -z "$str" ]]           # empty string
[[ -n "$str" ]]           # non-empty string
[]

Arrays

3 snippets
basics
fruits=("apple" "banana" "cherry")
echo ${fruits[0]}       # apple
echo ${fruits[@]}       # all elements
echo ${#fruits[@]}      # length → 3
echo ${!fruits[@]}      # all indices → 0 1 2
add, remove, slice
fruits+=("mango")           # append
unset fruits[1]            # remove banana
echo ${fruits[@]:1:2}    # slice: 2 items from index 1
iterate with index
for i in "${!fruits[@]}"; do
  echo "$i: ${fruits[$i]}"
done
{}

Dictionaries

4 snippets
declare & set
declare -A config
config["env"]="prod"
config["region"]="us-east-1"
config["replicas"]="3"
Requires bash 4+. Use declare -A before assigning.
read & check keys
echo ${config["env"]}         # prod
echo ${config[@]}             # all values
echo ${!config[@]}            # all keys
echo ${#config[@]}            # count → 3

# check if key exists
[[ -v config["env"] ]] && echo "exists"
iterate keys & values
for key in "${!config[@]}"; do
  echo "$key = ${config[$key]}"
done
inline declaration
declare -A ports=(
  ["http"]="80"
  ["https"]="443"
  ["ssh"]="22"
)
unset ports["http"]   # delete a key
Declare and populate in one shot.
if

Conditionals

3 snippets
if / elif / else
if [[ $score -ge 90 ]]; then
  echo "A"
elif [[ $score -ge 75 ]]; then
  echo "B"
else
  echo "C"
fi
file & number checks
[[ -f file ]] && [[ -s file ]]  # exists and non-empty
[[ -d /tmp ]]                   # is directory
[[ -x script.sh ]]              # is executable
[[ $a -lt $b ]]                  # less than
[[ $a -eq $b ]]                  # equal
case statement
case "$env" in
  prod)
    replicas=3 ;;
  staging)
    replicas=1 ;;
  *)
    echo "unknown env" && exit 1 ;;
esac
Cleaner than long if/elif chains for matching strings.
for

Loops

3 snippets
for range & array
for i in {1..5}; do echo $i; done

for f in "${files[@]}"; do
  echo "$f"
done
while & until
count=0
while [[ $count -lt 5 ]]; do
  (( count++ ))
done

until [[ -f /tmp/ready ]]; do
  sleep 1
done
read lines from file
while IFS= read -r line; do
  echo "$line"
done < file.txt
IFS= preserves whitespace. -r prevents backslash interpretation.
fn()

Functions

3 snippets
define & call
greet() {
  local name=$1
  echo "Hey, $name"
}
greet "Mayuresh"
return a value
add() { echo $(( $1 + $2 )); }
result=$(add 3 4)   # → 7
Bash functions return exit codes only. Use echo to pass back data.
local variables
process() {
  local input=$1
  local result
  result=$(echo "$input" | tr '[:lower:]' '[:upper:]')
  echo "$result"
}
Always use local inside functions to avoid polluting global scope.
-f

Options (getopts)

3 snippets
basic getopts
while getopts ":e:r:v" opt; do
  case $opt in
    e) ENV=$OPTARG ;;
    r) REGION=$OPTARG ;;
    v) VERBOSE=1 ;;
    ?) echo "unknown flag: -$OPTARG" && exit 1 ;;
  esac
done
shift $(( OPTIND - 1 ))   # remove parsed flags
The standard way to parse short flags in bash scripts.
usage pattern
usage() {
  echo "Usage: $0 -e <env> -r <region> [-v]"
  exit 1
}

[[ $# -eq 0 ]] && usage
Put this at the top. Clean help text is non-negotiable.
long options (manual)
while [[ $# -gt 0 ]]; do
  case $1 in
    --env)    ENV=$2;    shift 2 ;;
    --region) REGION=$2; shift 2 ;;
    --verbose) VERBOSE=1; shift ;;
    *) echo "unknown: $1"; exit 1 ;;
  esac
done
getopts doesn't support --long-flags natively. Parse them manually.
I/O

Input / Output

3 snippets
read user input
read -p "Enter name: " name
read -sp "Password: " pass   # silent
read -t 5 -p "Quick: " ans  # 5s timeout
read -a arr                   # read into array
redirects
cmd > out.txt      # stdout to file (overwrite)
cmd >> out.txt     # append
cmd 2> err.txt     # stderr only
cmd &> all.txt     # stdout + stderr
cmd 2>&1 | less    # pipe both streams
cmd < in.txt       # stdin from file
process substitution
diff <(sort file1) <(sort file2)
while IFS= read -r line; do
  echo $line
done < <(grep "ERROR" app.log)
Treats command output as a file descriptor. No subshell for while-read.
err

Error handling

3 snippets
strict mode
set -euo pipefail
IFS=$'\n\t'   # safer word splitting
Put this at the top of every production script.
trap for cleanup
cleanup() {
  echo "cleaning up..."
  rm -f /tmp/myfile
}
trap cleanup EXIT
trap 'echo "error on line $LINENO"' ERR
EXIT fires on any exit, clean, error, or signal.
guard: require tool
require() {
  command -v "$1" &>/dev/null || {
    echo "$1 not found" >&2; exit 1
  }
}
require kubectl
require terraform
!!

History & recall

4 snippets
quick recall
!!            # re-run last command
!$            # last argument of previous command
!*            # all arguments of previous command
!kubectl      # re-run last command starting with kubectl
!-2           # run the command 2 before last
These work interactively in your terminal session.
history search
history                  # list all history
history 20               # last 20 commands
history | grep docker    # search history
# Ctrl+R                 # interactive reverse search
history control
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoredups:erasedups
export HISTTIMEFORMAT="%F %T  "   # timestamp in history
Add to ~/.bashrc to tune history behavior.
fc, fix command
fc          # edit last command in $EDITOR
fc -l       # list recent history
fc 42       # edit and re-run history entry 42
Opens last command in your editor so you can fix and re-run.
...

Miscellaneous

6 snippets
arithmetic
echo $(( 10 + 5 * 2 ))   # 20
echo $(( 10 % 3 ))       # 1 (modulo)
(( x++ ))
(( x += 5 ))
result=$(bc <<< "scale=2; 10/3")  # float → 3.33
subshell vs source
( cd /tmp && ls )   # subshell, cwd unchanged after
source ~/.bashrc    # same shell, env changes stick
. ./helpers.sh      # same as source, POSIX compatible
. file.sh and source file.sh are identical, both run in current shell.
special variables
echo $0   # script name
echo $1   # first arg
echo $@   # all args (quoted individually)
echo $*   # all args (one string)
echo $#   # arg count
echo $?   # exit code of last command
echo $$   # PID of current shell
brace expansion
echo {a,b,c}.txt          # a.txt b.txt c.txt
mkdir -p app/{src,test,docs}
cp file.txt{,.bak}         # copy to file.txt.bak
echo {1..5}               # 1 2 3 4 5
echo {01..05}             # 01 02 03 04 05
tee & xargs
echo "hello" | tee output.txt
cmd 2>&1 | tee -a debug.log

cat pods.txt | xargs kubectl delete pod
find . -name "*.log" | xargs rm -f
tee writes to a file and stdout simultaneously. xargs builds command lines from stdin.
sleep, date, printf
sleep 2                              # wait 2 seconds
date +"%Y-%m-%d %H:%M:%S"            # formatted date
today=$(date +%Y%m%d)
printf "%-15s %5d\n" "pods" 42      # formatted output