Modules · wrap-up
Cheat sheet & self-check
Every command from this section on one page.
Basics
#!/usr/bin/env bash | First line: run this file with bash |
chmod +x script.sh && ./script.sh | Make executable and run |
bash -x script.sh | Run with a trace of every command |
name="Asha" | Assign (no spaces around =) |
echo "Hello, $name" | Use a variable (inside double quotes) |
Quoting
"$var" | Double quotes: variables expand, no word splitting (use by default) |
'$var' | Single quotes: completely literal |
today=$(date +%F) | Command substitution: capture output |
echo "$?" | Exit status of the last command (0 = success) |
Tests inside [[ ]]
[[ -f file ]] / [[ -d dir ]] | File exists / directory exists |
[[ -z "$v" ]] / [[ -n "$v" ]] | Empty / not empty |
[[ "$a" == "$b" ]] | Strings equal |
[[ $name == web-* ]] | Pattern match |
(( n > 10 )) | Numeric comparison |
Loops
for host in web-01 web-02; do …; done | Loop over words |
for f in /var/log/*.log; do …; done | Loop over files (glob) |
for i in {1..5}; do …; done | Loop over a range |
while IFS= read -r line; do …; done < file | Read a file line by line, safely |
arr=(a b c); for x in "${arr[@]}"; do …; done | Loop over an array |
Arguments
$0 | The script's name |
$1, $2 … | Positional arguments |
"$@" | All arguments, each kept whole (quote it!) |
$# | Number of arguments |
shift | Drop $1, move the rest down |
${1:-default} | Use a default if $1 is empty |
Functions
log() { printf '%s %s\n' "$(date +%T)" "$*" >&2; } | A logging helper that writes to stderr |
local name="$1" | Variable visible only inside the function |
return 1 | Function failure status |
result=$(my_func arg) | Capture a function's output |
sed
sed -n '10,20p' file | Print lines 10–20 |
sed 's/old/new/g' file | Replace every 'old' with 'new' (prints the result) |
sed -i.bak 's/^Port 22$/Port 2222/' file | Edit in place, keeping file.bak |
sed '/^#/d; /^$/d' file | Delete comments and blank lines |
awk
awk '{print $1, $3}' file | Print columns 1 and 3 |
awk -F: '$3 >= 1000 {print $1}' /etc/passwd | Users with UID ≥ 1000 |
awk '{sum += $2} END {print sum}' file | Sum a column |
awk '$9 >= 500' access.log | wc -l | Count 5xx lines (combined log format) |
jq
jq '.items | length' | Count items |
jq -r '.items[].metadata.name' | One name per line (raw) |
jq -r '.items[] | select(.status.phase != "Running") | .metadata.name' | Filter objects |
jq -r '.[] | [.name, .size] | @csv' | Output CSV |
Strict mode
set -euo pipefail | Stop on errors, unset variables, and failures inside pipelines |
trap 'echo "failed at line $LINENO" >&2' ERR | Report where it failed |
trap cleanup EXIT | Always run cleanup, success or failure |
${VAR:?VAR must be set} | Abort with a message if VAR is empty |
Safety patterns
tmp=$(mktemp -d) | A private temporary directory |
exec 9>/var/lock/myjob.lock; flock -n 9 || exit 0 | Only one copy runs at a time |
mkdir -p, ln -sfn, cp -n | Idempotent building blocks |
rm -rf -- "${dir:?}"/ | Refuse to run if dir is empty |
cron
crontab -e | Edit your user's crontab |
crontab -l | List it |
*/15 * * * * /opt/bin/job.sh | Every 15 minutes |
30 2 * * 1-5 /opt/bin/job.sh | 02:30 on weekdays |
0 3 1 * * /opt/bin/job.sh | 03:00 on the 1st of every month |
systemd timers
systemctl list-timers | All timers with their next and last runs |
systemd-analyze calendar 'Mon..Fri 02:30' | Check an OnCalendar expression |
sudo systemctl start backup.service | Run the job now, on demand |
journalctl -u backup.service --since today | The job's logs |
Lint & format
shellcheck script.sh | Find bugs: unquoted variables, bad tests, unused vars… |
shellcheck -x script.sh | Also follow 'source'd files |
shfmt -d -i 2 script.sh | Show formatting differences (2-space indent) |
shfmt -w -i 2 script.sh | Rewrite the file formatted |
bats
bats tests/ | Run all .bats test files |
run my_function arg | Call something and capture $status and $output |
[ "$status" -eq 0 ] | Assert success |
[[ "$output" == *"expected"* ]] | Assert output contains text |
Exit codes used by healthcheck.sh
0 | All checks passed |
1 | At least one check failed |
2 | Usage error |
3 | A required tool is missing |
Run it
./healthcheck.sh -c checks.conf | Run checks from a file |
./healthcheck.sh -c checks.conf -w https://hooks.example.com/… | Also send an alert on failure |
systemctl list-timers healthcheck.timer | When it runs next |