Levels 21–28: Expert
Level 21: Cron & Scheduling
Section titled “Level 21: Cron & Scheduling”Automate everything. If you do it twice, cron it.
Crontab format:
MIN HOUR DAY MONTH WEEKDAY command * * * * * /usr/local/bin/script.sh| Field | Range | Special |
|---|---|---|
| Minute | 0–59 | |
| Hour | 0–23 | |
| Day of month | 1–31 | |
| Month | 1–12 (or Jan–Dec) | |
| Day of week | 0–7 (0 and 7 = Sunday) |
Special values:
| Syntax | Meaning |
|---|---|
* |
Every value |
*/15 |
Every 15 (step) |
1-5 |
Range: 1 through 5 |
1,3,5 |
List: 1, 3, and 5 |
Common patterns:
# Every minute* * * * * /usr/local/bin/healthcheck.sh
# Every day at 2am0 2 * * * /usr/local/bin/backup.sh
# Every 15 minutes*/15 * * * * /usr/local/bin/sync.sh
# 9am weekdays (Mon–Fri)0 9 * * 1-5 /usr/local/bin/report.sh
# First day of every month at midnight0 0 1 * * /usr/local/bin/monthly.shcrontab commands:
| Command | Action |
|---|---|
crontab -l |
List current crontab |
crontab -e |
Edit crontab |
crontab -r |
Remove ALL cron jobs (no confirmation) |
crontab -u user -l |
List another user’s crontab (root) |
at: one-time scheduled tasks:
echo '/usr/local/bin/restart.sh' | at 23:00echo '/usr/local/bin/script.sh' | at 2:30 AM tomorrowatq # List pending at jobsatrm 3 # Remove at job #3Level 22: Logs & Monitoring
Section titled “Level 22: Logs & Monitoring”When something breaks at 3am, logs are your flashlight.
| Command | What it does |
|---|---|
tail -f /var/log/syslog |
Follow log file live |
tail -f app.log | grep --line-buffered ERROR |
Follow with live filter |
journalctl |
All systemd journal entries |
journalctl -f |
Follow journal live |
journalctl -u nginx |
Logs for specific service |
journalctl -fu nginx |
Follow specific service live |
journalctl --since '1 hour ago' |
Time-filtered logs |
journalctl --since '2024-01-15 08:00' --until '2024-01-15 09:00' |
Time window |
journalctl -b |
Logs from current boot only |
journalctl -b -1 |
Logs from previous boot |
journalctl -n 50 |
Last 50 lines |
journalctl -p err |
Error priority and above |
logger 'message' |
Write to system log from script |
logger -t mytag 'message' |
Write with tag |
Level 23: Package Management
Section titled “Level 23: Package Management”Same concepts, different commands: know your distro.
Debian / Ubuntu (apt):
sudo apt update # Refresh package indexsudo apt upgrade # Upgrade installed packagessudo apt full-upgrade # Upgrade + handle dependency changessudo apt install -y packagename # Install (-y = no prompts)sudo apt remove packagename # Remove (keep config files)sudo apt purge packagename # Remove + delete config filessudo apt autoremove # Remove orphaned dependenciesapt search keyword # Search available packagesapt show packagename # Show package detailsdpkg -l | grep packagename # List installed packageRHEL / Fedora / CentOS (dnf/yum):
sudo dnf update # Update all packagessudo dnf install -y packagename # Installsudo dnf remove packagename # Removednf search keyword # Searchdnf info packagename # Show detailsrpm -qa | grep packagename # List installedmacOS (Homebrew):
brew update # Update Homebrew itselfbrew upgrade # Upgrade all packagesbrew install packagename # Installbrew uninstall packagename # Removebrew search keyword # Searchbrew info packagename # Show detailsbrew list # List installedLevel 24: Compression Deep Dive
Section titled “Level 24: Compression Deep Dive”Different formats for different needs. Know the trade-offs.
| Format | Speed | Ratio | Best for |
|---|---|---|---|
gzip (.gz) |
Fast | Good | Log rotation, web serving, general use |
bzip2 (.bz2) |
Moderate | Better | Source code archives |
xz (.xz) |
Slow | Best | Long-term storage, kernel releases |
zip (.zip) |
Fast | Moderate | Windows compatibility |
| Command | What it does |
|---|---|
gzip file |
Compress (replaces original) |
gzip -k file |
Compress, keep original |
gunzip file.gz |
Decompress |
zcat file.gz |
Read compressed file (stdout) |
zgrep pattern file.gz |
grep inside compressed file |
bzip2 file |
Compress with bzip2 |
bunzip2 file.bz2 |
Decompress bzip2 |
bzcat file.bz2 |
Read bzip2 compressed |
xz file |
Compress with xz |
xz -9 file |
Maximum compression (slowest) |
unxz file.xz |
Decompress xz |
xzcat file.xz |
Read xz compressed |
zip -r out.zip dir/ |
Create zip archive |
unzip out.zip |
Extract zip |
unzip -l out.zip |
List zip contents |
tar + compression combined:
tar -czf archive.tar.gz dir/ # gzip (.tar.gz / .tgz)tar -cjf archive.tar.bz2 dir/ # bzip2 (.tar.bz2)tar -cJf archive.tar.xz dir/ # xz (.tar.xz)tar -xzf archive.tar.gz # extract gziptar -xJf archive.tar.xz # extract xztar -tzf archive.tar.gz # list contentsLevel 25: Bash String Processing
Section titled “Level 25: Bash String Processing”Built-in shell operations: no subprocess, no external commands.
| Syntax | Result | Example |
|---|---|---|
${#var} |
String length | ${#NAME} → 9 |
${var:0:4} |
Substring from offset 0, length 4 | ${NAME:0:4} → Bash |
${var: -3} |
Last 3 characters (note the space) | ${NAME: -4} → uest |
${var%pattern} |
Strip shortest suffix match | ${FILE%.gz} → file.tar |
${var%%pattern} |
Strip longest suffix match | ${FILE%%.tar*} → file |
${var#pattern} |
Strip shortest prefix match | ${PATH#*/} |
${var##pattern} |
Strip longest prefix match | ${PATH##*/} → filename |
${var/find/replace} |
Replace first occurrence | ${TEXT/cat/dog} |
${var//find/replace} |
Replace all occurrences | ${TEXT//cat/dog} |
${var^^} |
Uppercase all (bash 4+) | ${name^^} → NAME |
${var,,} |
Lowercase all (bash 4+) | ${NAME,,} → name |
${var:-default} |
Use default if var is unset/empty | ${PORT:-8080} |
${var:=default} |
Assign default if unset/empty | ${DIR:=/tmp} |
printf formatting:
printf '%-20s %5d\n' "Alice" 95 # Left-aligned name, right-aligned numberprintf '%.2f\n' 3.14159 # 2 decimal places: 3.14printf '%05d\n' 42 # Zero-padded: 00042printf '%s\n' "${array[@]}" # One element per lineLevel 26: Arrays in Bash
Section titled “Level 26: Arrays in Bash”Store multiple values. Iterate safely. No string-splitting bugs.
Indexed arrays:
# CreateSERVERS=(web01 web02 db01)
# Accessecho ${SERVERS[0]} # web01 (zero-indexed)echo ${SERVERS[-1]} # db01 (last element, bash 4.2+)echo ${SERVERS[@]} # all elementsecho ${#SERVERS[@]} # element count: 3
# AppendSERVERS+=(db02)
# Loop: always quote "${arr[@]}"for s in "${SERVERS[@]}"; do echo "Checking $s..."done
# Sliceecho ${SERVERS[@]:1:2} # elements 1 and 2: web02 db01Associative arrays (bash 4+):
declare -A PORTS=([http]=80 [https]=443 [ssh]=22)
echo ${PORTS[http]} # 80echo ${!PORTS[@]} # all keys: http https sshecho ${PORTS[@]} # all values: 80 443 22
for key in "${!PORTS[@]}"; do echo "$key → ${PORTS[$key]}"doneLevel 27: Functions & Error Handling
Section titled “Level 27: Functions & Error Handling”Production scripts don’t silently fail.
The safety header: put this at the top of every production script:
#!/bin/bashset -euo pipefail| Option | Effect |
|---|---|
set -e |
Exit immediately on any non-zero exit code |
set -u |
Treat unset variables as an error |
set -o pipefail |
Fail if any command in a pipe fails |
Functions:
# Definegreet() { local name="$1" # local variables don't leak echo "Hello, $name!"}
# Callgreet Tony
# Return values (via exit code)is_root() { [ "$(id -u)" -eq 0 ] && return 0 || return 1}
if is_root; then echo "Running as root"fiExit codes:
# $? = exit code of last commandls /nonexistent 2>/dev/nullecho $? # 2 (non-zero = failure)
# Always check immediately: $? changes after every commandcp src.txt dst.txtif [ $? -ne 0 ]; then echo "Copy failed" >&2 exit 1fitrap: guaranteed cleanup:
TMPDIR=$(mktemp -d)
cleanup() { rm -rf "$TMPDIR" echo "Cleaned up."}
trap cleanup EXIT # Runs on any exittrap cleanup INT TERM # Also runs on Ctrl+C / kill
# Do work in $TMPDIR...trap ... EXIT fires on: normal completion, set -e error, SIGTERM, Ctrl+C. The cleanup always runs.
Level 28: Systemd & Services
Section titled “Level 28: Systemd & Services”systemd manages everything that runs in the background on modern Linux.
| Command | What it does |
|---|---|
systemctl status nginx |
Show service status, PID, memory, recent logs |
sudo systemctl start nginx |
Start service now |
sudo systemctl stop nginx |
Stop service now |
sudo systemctl restart nginx |
Stop then start (brief downtime) |
sudo systemctl reload nginx |
Re-read config without restart (if supported) |
sudo systemctl enable nginx |
Enable autostart on boot |
sudo systemctl disable nginx |
Disable autostart on boot |
sudo systemctl enable --now nginx |
Enable AND start in one command |
sudo systemctl disable --now nginx |
Disable AND stop in one command |
systemctl list-units --type=service |
List all service units |
systemctl --state=failed |
Show only failed units |
journalctl -u nginx |
Logs for this service |
journalctl -fu nginx |
Follow logs live |
sudo systemctl daemon-reload |
Reload unit files after editing |
Minimal unit file (/etc/systemd/system/myapp.service):
[Unit]Description=My ApplicationAfter=network.target
[Service]Type=simpleUser=myappExecStart=/usr/local/bin/myapp --config /etc/myapp/config.ymlRestart=on-failureRestartSec=5
[Install]WantedBy=multi-user.targetAfter creating or editing a unit file:
sudo systemctl daemon-reloadsudo systemctl enable --now myappjournalctl -fu myapp # Watch it startService states:
| State | Meaning |
|---|---|
active (running) |
Running normally |
active (exited) |
Ran once and exited cleanly (oneshot) |
inactive (dead) |
Not running |
failed |
Exited with error or crashed |
activating |
In the process of starting |