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-liner | What it does |
|---|---|
find . -type f -name '*.md' | All markdown files, recursive. |
find . -type f -newer reference.txt | Files modified after reference.txt. |
find . -type f -size +10M | Files larger than 10 MB. |
find . -type f -mtime -7 | Files modified in the last 7 days. |
find . -type f -name '*.log' -delete | Delete matched files in one pass. |
find . -type f -name '*.tmp' -exec rm {} + | Same, batched for fewer rm calls. |
find . -type d -empty -delete | Prune 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-liner | What it does |
|---|---|
sed -i 's/old/new/g' file.txt | GNU sed in-place replacement (Linux). |
sed -i '' 's/old/new/g' file.txt | BSD 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.txt | Lowercase a file. |
awk '{print $2, $1}' file | Swap 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.
| Expansion | Result 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-liner | What it does |
|---|---|
cmd & | Run in background. |
jobs | List background jobs in this shell. |
wait | Block until all background jobs finish. |
wait $PID | Block until one specific PID exits. |
disown %1 | Detach job 1 from the shell; survives logout. |
trap 'rm -rf "$TMP"' EXIT | Cleanup runs on normal and abnormal exit. |
trap 'echo interrupted; exit 130' INT | Custom 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-liner | What it does |
|---|---|
(( count++ )) | Increment in arithmetic context. |
echo $(( 3 * (4 + 5) )) | Inline arithmetic; prints 27. |
echo "scale=2; 7/3" | bc | Float 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/$dirfails silently ifcdfails. Writecd "$dir" \|\| exit 1or run withset -e.- Pipelines hide exit codes from earlier commands.
set -o pipefailfixes it. for f in *.mdreturns the literal*.mdif nothing matches. Setshopt -s nullglobfirst.readstrips leading and trailing whitespace and interprets backslashes. Useread -r linefor raw input.$(...)and backticks both subshell out. Prefer$(...); it nests and quotes cleanly.