Bash

Moving around, wrangling text, and writing small scripts that don't fall over.

Files & navigation

ls -lah
List everything, human-readable sizes
cd -
Jump back to the previous directory
mkdir -p a/b/c
Create nested folders in one go
cp -r src dst
Copy a directory recursively
find . -name "*.log"
Find files by name pattern
du -sh *
Size of each item in the current folder

Pipes & redirection

cmd > out.txt
Overwrite file with stdout
cmd >> out.txt
Append stdout to file
cmd 2>&1
Send stderr to wherever stdout goes
cmd1 | cmd2
Feed output of one command into another
cmd | tee log.txt
Print and save at the same time
cmd < input.txt
Read stdin from a file

Text processing

grep -rn "TODO" src/
Recursive search with line numbers
sed -i 's/foo/bar/g' f
Replace in place (macOS: sed -i '')
awk '{print $2}'
Print the second column
sort | uniq -c | sort -rn
Count occurrences, most frequent first
cut -d, -f1,3
Columns 1 and 3 of a CSV
tail -f app.log
Follow a log file as it grows

Processes

cmd &
Run in the background
jobs / fg %1
List background jobs / bring one forward
ps aux | grep node
Find a running process
kill -9 <pid>
Force-terminate a process
lsof -i :3000
What is listening on port 3000?

Keyboard shortcuts

Ctrl + R
Search command history
Ctrl + A / Ctrl + E
Start / end of line
Ctrl + U / Ctrl + K
Cut to start / end of line
Ctrl + L
Clear the screen
!!
Repeat last command (sudo !!)

Variables & expansion

${VAR:-default}
Use default if VAR is unset
${#VAR}
Length of the string
${FILE%.txt}
Strip suffix
$(cmd)
Substitute a command's output
$?
Exit code of the last command
Tip: Start every script with set -euo pipefail so it stops on errors, unset variables and failed pipes instead of charging ahead.

Script skeleton

#!/usr/bin/env bash
set -euo pipefail

usage() { echo "usage: $0 <input-dir>"; exit 1; }
[[ $# -eq 1 ]] || usage

dir="$1"
for f in "$dir"/*.csv; do
  [[ -e "$f" ]] || continue
  echo "processing $(basename "$f")"
  wc -l < "$f"
done