bash.md

Bash #

The default shell on most Linux systems and a capable scripting language. Commands are words separated by spaces; everything else is quoting, expansion, and control flow.

pwd                              # print working directory
cd /var/log                      # change directory ( cd - jumps back )
ls -lah                          # long list, all files, human sizes
find . -name '*.log' -type f     # search by name, files only
cp -r src/ bak/                  # copy a directory
mv old.txt new.txt               # move or rename
rm -rf build/                    # delete recursively, no prompt (careful!)
tail -f app.log                  # follow a file as it grows

Variables & expansion #

name="turtle"                    # no spaces around =
count=42
echo "$name has $count items"    # double quotes expand variables
echo '$name stays literal'       # single quotes do not expand
greeting="Hello, ${name}!"       # braces disambiguate the name
readonly PI=3.14                 # constant
export PATH="$HOME/bin:$PATH"    # export to child processes
unset count                      # remove a variable

Parameter expansion #

file="report.tar.gz"
echo "${file%.gz}"               # report.tar   (strip shortest suffix)
echo "${file%%.*}"               # report       (strip longest suffix)
echo "${file#*.}"                # tar.gz       (strip shortest prefix)
echo "${name:-guest}"            # default if unset/empty
echo "${name:=guest}"            # default AND assign
echo "${#name}"                  # length of the value
echo "${path//\//_}"             # replace all / with _

Command substitution & arithmetic #

today=$(date +%F)                # capture command output
files=$(ls | wc -l)
echo "There are $files files as of $today"
n=$(( (3 + 4) * 2 ))             # integer arithmetic
(( count++ ))                    # increment in place
echo $(( RANDOM % 6 + 1 ))       # dice roll 1-6

Tests & conditionals #

if [[ -f "$config" ]]; then       # file exists and is regular
  source "$config"
elif [[ -d "$config" ]]; then     # is a directory
  echo "expected a file"
else
  echo "no config"
fi

[[ -z "$x" ]]                     # string is empty
[[ -n "$x" ]]                     # string is non-empty
[[ "$a" == "$b" ]]                # string equality
[[ "$n" -gt 10 ]]                 # numeric: -eq -ne -lt -le -gt -ge
[[ "$s" =~ ^[0-9]+$ ]]            # regex match
cmd && echo ok || echo failed     # run on success / on failure

Loops #

for f in *.txt; do                # glob expands to matching files
  echo "processing $f"
done

for i in {1..5}; do echo "$i"; done     # brace range
for ((i = 0; i < 5; i++)); do echo "$i"; done   # C-style

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

until ping -c1 host &>/dev/null; do sleep 1; done

Functions #

greet() {
  local who="${1:-world}"         # local keeps it out of global scope
  echo "Hello, $who"
  return 0                        # exit status, not a value
}

greet "turtle"                    # call with arguments
result=$(greet)                   # capture echoed output

# $1 $2 ...  positional args   $#  count   $@  all args   $?  last status

Arrays #

fruits=(apple banana cherry)      # indexed array
echo "${fruits[0]}"               # apple
echo "${fruits[@]}"               # all elements
echo "${#fruits[@]}"              # length
fruits+=(date)                    # append
for f in "${fruits[@]}"; do echo "$f"; done

declare -A color                  # associative array (bash 4+)
color[sky]=blue
echo "${color[sky]}"

Redirection & pipes #

cmd > out.txt                     # stdout to file (overwrite)
cmd >> out.txt                    # append
cmd 2> err.txt                    # stderr to file
cmd > out.txt 2>&1                # both to one file
cmd &> all.txt                    # shorthand for both
cmd < in.txt                      # stdin from file
a | b | c                         # pipe stdout through a chain
diff <(sort a) <(sort b)          # process substitution
cmd | tee log.txt                 # to screen and file

Text processing #

grep -rn "TODO" src/              # recursive, with line numbers
grep -i "error" log | wc -l       # case-insensitive count
sed 's/foo/bar/g' file            # substitute all on each line
awk '{ print $1, $3 }' data       # print fields 1 and 3
awk -F, '{ sum += $2 } END { print sum }' nums.csv
cut -d: -f1 /etc/passwd           # first colon-delimited field
sort file | uniq -c | sort -rn    # frequency count, descending

Safe scripting #

#!/usr/bin/env bash
set -euo pipefail                 # exit on error, unset var, pipe failure
IFS=$'\n\t'                       # safer word splitting

trap 'echo "cleaning up"; rm -f "$tmp"' EXIT   # run on exit
tmp=$(mktemp)

main() {
  [[ $# -ge 1 ]] || { echo "usage: $0 <file>" >&2; exit 1; }
  process "$1"
}

main "$@"                         # forward all arguments

Jobs & history #

long_task &                       # run in the background
jobs                              # list background jobs
fg %1                             # bring job 1 to foreground
kill %1                           # signal a job
nohup ./run.sh &                  # survive logout
Ctrl-C   Ctrl-Z                   # interrupt / suspend current job
!!       !$                       # last command / its last argument
history | grep ssh                # search command history

© 2026 anguishedturtle.comBit Night RunnerSupportPrivacy