Bash Cheatsheet

Arrays

Use this Bash reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Indexed Arrays — Declaration and Assignment

# Literal assignment
arr=("apple" "banana" "cherry")

# Declare then assign
declare -a arr
arr[0]="first"
arr[1]="second"
arr[5]="sixth"    # sparse: indices 2-4 don't exist

# Append
arr+=("new item")
arr+=("x" "y")    # append multiple

# From command output
arr=( $(ls *.txt) )           # word-split (fails with spaces in names)
mapfile -t arr < <(ls *.txt)  # safer: one element per line
readarray -t arr < file.txt   # alias for mapfile

Accessing Elements

arr=("a" "b" "c" "d")

echo ${arr[0]}          # first element: "a"
echo ${arr[-1]}         # last element: "d" (Bash 4.3+)
echo ${arr[-2]}         # second to last: "c"
echo ${arr[@]}          # all elements (each quoted separately)
echo ${arr[*]}          # all elements (joined with IFS when quoted)
echo "${arr[@]:1:2}"    # slice: elements 1 and 2 → "b c"
echo "${arr[@]: -2}"    # last 2 elements → "c d"
echo ${#arr[@]}         # number of elements: 4
echo ${#arr[2]}         # length of element at index 2: 1
echo ${!arr[@]}         # all indices: 0 1 2 3

Iterating Arrays

arr=("red" "green" "blue")

# Iterate values
for item in "${arr[@]}"; do
  echo "$item"
done

# Iterate with index
for i in "${!arr[@]}"; do
  echo "[$i] = ${arr[$i]}"
done

# C-style with length
for (( i=0; i<${#arr[@]}; i++ )); do
  echo "$i: ${arr[$i]}"
done

Modifying Arrays

arr=("a" "b" "c" "d")

# Set element
arr[1]="B"

# Append single
arr+=("e")

# Append multiple
arr+=("f" "g")

# Delete element (leaves hole — index remains, value becomes empty)
unset arr[2]
echo "${arr[@]}"     # a B  d e f g  (gap at index 2)
echo "${!arr[@]}"    # 0 1 3 4 5 6   (index 2 gone)

# Re-index to fill holes
arr=( "${arr[@]}" )

# Delete entire array
unset arr

# Slice (copy subset)
slice=( "${arr[@]:1:3}" )    # elements 1,2,3

# Sort (no built-in; use a subshell + printf trick)
IFS=$'\n' sorted=($(sort <<< "${arr[*]}"))

Array Manipulation Patterns

arr=("apple" "banana" "cherry" "date")

# Reverse
reversed=()
for (( i=${#arr[@]}-1; i>=0; i-- )); do
  reversed+=("${arr[$i]}")
done

# Filter (keep matching)
filtered=()
for item in "${arr[@]}"; do
  [[ "$item" == *a* ]] && filtered+=("$item")
done
echo "${filtered[@]}"   # apple banana date

# Map (transform)
upper=()
for item in "${arr[@]}"; do
  upper+=("${item^^}")
done

# Join with delimiter
(IFS=','; echo "${arr[*]}")    # apple,banana,cherry,date
printf '%s,' "${arr[@]}"       # apple,banana,cherry,date, (trailing comma)
printf '%s\n' "${arr[@]}" | paste -sd,  # clean join

Associative Arrays (Bash 4+)

# Must explicitly declare
declare -A map

# Assign
map["key"]="value"
map[host]="localhost"
map[port]="5432"

# Literal initialization
declare -A colors=(
  [red]="#ff0000"
  [green]="#00ff00"
  [blue]="#0000ff"
)

# Access
echo ${map[host]}         # localhost
echo ${map["key"]}        # value
echo ${map[missing]}      # empty string (no error)

# All keys
echo ${!map[@]}           # host port key

# All values
echo ${map[@]}            # values in unspecified order

# Count
echo ${#map[@]}           # 3

# Check if key exists
[[ -v map[host] ]] && echo "host is set"
[[ ${map[host]+x} ]] && echo "host is set"  # POSIX-ish

# Delete a key
unset map[port]

# Delete all
unset map

Iterating Associative Arrays

declare -A scores=([alice]=95 [bob]=82 [carol]=91)

# Values
for val in "${scores[@]}"; do
  echo "$val"
done

# Keys
for key in "${!scores[@]}"; do
  echo "$key"
done

# Key-value pairs
for key in "${!scores[@]}"; do
  echo "$key = ${scores[$key]}"
done

# Sorted by key
for key in $(echo "${!scores[@]}" | tr ' ' '\n' | sort); do
  echo "$key: ${scores[$key]}"
done

mapfile / readarray

# Read lines of a file into array (Bash 4+)
mapfile -t lines < file.txt
readarray -t lines < file.txt   # same thing

# From command
mapfile -t files < <(find . -name "*.sh")

# With offset (start at index 2)
mapfile -t -O 2 arr < file.txt

# With count (read only 5 lines)
mapfile -t -n 5 arr < file.txt

# Callback on each line
mapfile -t -C 'process_line' -c 1 arr < file.txt

echo "${lines[0]}"   # first line
echo "${#lines[@]}"  # total lines

Array Copying and Passing

orig=("x" "y" "z")

# Copy array
copy=("${orig[@]}")

# Pass to function as arguments
process() {
  local -a local_arr=("$@")
  echo "${local_arr[@]}"
}
process "${orig[@]}"

# Pass by name (nameref, Bash 4.3+)
process_ref() {
  declare -n _arr=$1
  echo "${_arr[@]}"
}
process_ref orig

# Return array from function via nameref
get_data() {
  declare -n _out=$1
  _out=("result1" "result2")
}
declare -a output
get_data output
echo "${output[@]}"

Common Gotchas

arr=("a" "b" "c")

# WRONG: missing quotes causes word splitting
for i in ${arr[@]}; do ...   # breaks on elements with spaces

# RIGHT:
for i in "${arr[@]}"; do ...

# WRONG: $arr alone expands to only arr[0]
echo $arr          # only "a"
echo ${arr}        # only "a"

# RIGHT:
echo "${arr[@]}"   # all elements

# Sparse arrays — length vs max index
arr[100]="late"
echo ${#arr[@]}    # 1 (one element), not 101
echo ${!arr[@]}    # 100 (the actual index)

# [[ -v ]] is the right way to check element existence
[[ -v arr[5] ]] && echo "index 5 exists"