Overview

Bash carries a lot of weight in day-to-day plumbing. This card collects the one-liners worth memorizing, grouped by task. The full rules (set -euo pipefail, quoting, error handling) live in shell; this page is the lookup table for “what was that flag again?“. All examples assume Bash 5 with set -euo pipefail.

Find files

Find before rename, find before delete, find before pipe.

One-linerWhat it does
find . -type f -name '*.md'All markdown files, recursive.
find . -type f -newer reference.txtFiles modified after reference.txt.
find . -type f -size +10MFiles larger than 10 MB.
find . -type f -mtime -7Files modified in the last 7 days.
find . -type f -name '*.log' -deleteDelete matched files in one pass.
find . -type f -name '*.tmp' -exec rm {} +Same, batched for fewer rm calls.
find . -type d -empty -deletePrune empty directories.
grep -rIn "pattern" .Recursive grep, line numbers, skip binaries.
rg -n "pattern"ripgrep; faster and respects .gitignore.

Use find ... -print0 \| xargs -0 when filenames may contain spaces or newlines.

Find and replace

In-place edits should be staged in git first.

One-linerWhat it does
sed -i 's/old/new/g' file.txtGNU sed in-place replacement (Linux).
sed -i '' 's/old/new/g' file.txtBSD sed in-place replacement (macOS).
find . -name '*.md' -exec sed -i 's/old/new/g' {} +Replace across many files.
grep -rl "old" . | xargs sed -i 's/old/new/g'Replace only in files that contain “old”.
tr 'A-Z' 'a-z' < file > lower.txtLowercase a file.
awk '{print $2, $1}' fileSwap columns 1 and 2.

sed -i differs between BSD and GNU. Write sed -i.bak and keep the backup for portability.

Parameter expansion

The shell offers cheap string ops; reach for these before sed.

ExpansionResult for f=path/to/file.tar.gz
${f##*/}file.tar.gz (basename).
${f%/*}path/to (dirname).
${f%.*}path/to/file.tar (drop last extension).
${f%%.*}path/to/file (drop all extensions).
${f#*.}tar.gz (drop everything up to the first dot).
${f//\//-}path-to-file.tar.gz (replace all / with -).
${name:-default}Use default if name unset or empty.
${name:?error message}Exit with an error if name is unset.
${#name}Length of name.

Pure-bash string ops avoid forking a subprocess; in tight loops the win is real.

Jobs and traps

Background work needs cleanup; traps make cleanup unconditional.

One-linerWhat it does
cmd &Run in background.
jobsList background jobs in this shell.
waitBlock until all background jobs finish.
wait $PIDBlock until one specific PID exits.
disown %1Detach job 1 from the shell; survives logout.
trap 'rm -rf "$TMP"' EXITCleanup runs on normal and abnormal exit.
trap 'echo interrupted; exit 130' INTCustom handler for Ctrl-C.
nohup cmd > out.log 2>&1 &Detach from controlling terminal.

Always trap EXIT for temp-directory cleanup. Use mktemp -d to create the directory.

Arithmetic and tests

Bash arithmetic is integer-only; use bc or awk for floats.

One-linerWhat it does
(( count++ ))Increment in arithmetic context.
echo $(( 3 * (4 + 5) ))Inline arithmetic; prints 27.
echo "scale=2; 7/3" | bcFloat division; prints 2.33.
[[ -f file ]]True if file exists and is regular.
[[ -d dir ]]True if directory exists.
[[ -z $var ]]True if variable is empty or unset.
[[ $a == "yes" ]]String equality.
[[ $s =~ ^[0-9]+$ ]]Regex match; numeric string.

Prefer [[ ... ]] over [ ... ]. It does not split words, does pattern and regex matching, and does not need quoting around variables.

Common gotchas

  • Unquoted variables word-split. Write "$var" everywhere unless you intentionally want splitting.
  • cd /tmp/$dir fails silently if cd fails. Write cd "$dir" \|\| exit 1 or run with set -e.
  • Pipelines hide exit codes from earlier commands. set -o pipefail fixes it.
  • for f in *.md returns the literal *.md if nothing matches. Set shopt -s nullglob first.
  • read strips leading and trailing whitespace and interprets backslashes. Use read -r line for raw input.
  • $(...) and backticks both subshell out. Prefer $(...); it nests and quotes cleanly.