Stage 3 · Build
Shell Scripting & Automation
cron & at Jobs
crontab syntax, environment pitfalls, flock locking, and one-shot scheduling with at.
crontab Syntax
# crontab format
# ┌───────── minute (0-59)
# │ ┌────── hour (0-23)
# │ │ ┌─── day of month (1-31)
# │ │ │ ┌── month (1-12)
# │ │ │ │ ┌── day of week (0-7, 0=7=Sun)
# │ │ │ │ │
# * * * * * command to execute
# Examples
30 2 * * * /usr/bin/backup.sh # Daily at 2:30 AM
0 */4 * * * /usr/bin/cleanup.sh # Every 4 hours
0 9-17 * * 1-5 /usr/bin/work-hours.sh # Weekdays 9-5
0 0 1 * * /usr/bin/monthly-report.sh # First of month
0 0 * * 0 /usr/bin/weekly-cleanup.sh # Every Sunday
# Edit crontab
crontab -e
# List crontab entries
crontab -l
# Remove all crontab entries
crontab -r
# Specific user's crontab
sudo crontab -u username -lcrontab is per-user. The root crontab is separate from regular user crontabs. Use /etc/cron.d/ for system-wide scheduled tasks.
Cron Environment
# Cron has a minimal environment
# PATH=/usr/bin:/bin
# SHELL=/bin/sh
# No user environment variables
# Common pitfalls:
# - Commands not found (limited PATH)
# - No email output (no terminal)
# - Different working directory
# Fix: set environment at top of crontab
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/root
MAILTO=admin@example.com
# Or use full paths
0 3 * * * /usr/bin/python3 /opt/scripts/backup.py
# Source environment in script
#!/usr/bin/env bash
source /etc/profile.d/app.sh
# ... rest of script
# Redirect output
0 3 * * * /usr/bin/backup.sh >> /var/log/backup.log 2>&1Cron runs with a minimal environment. Always use full paths, set PATH, and redirect output for debugging.
flock Locking
# Problem: job takes longer than interval, jobs overlap
# Solution 1: flock (recommended)
0 * * * * flock -n /var/run/backup.lock /usr/bin/backup.sh
# -n: non-blocking, fail immediately if lock is held
# Solution 2: flock with timeout
0 * * * * flock -w 300 /var/run/backup.lock /usr/bin/backup.sh
# -w: wait up to 300 seconds for lock
# Solution 3: Lock in script
#!/usr/bin/env bash
exec 200>/var/run/backup.lock
flock -n 200 || { echo "Already running"; exit 1; }
# ... rest of script
# Solution 4: PID file
#!/usr/bin/env bash
PIDFILE=/var/run/backup.pid
if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
echo "Already running"
exit 1
fi
echo $$ > "$PIDFILE"
trap 'rm -f "$PIDFILE"' EXIT
# ... rest of scriptflock provides file-based locking. Use -n for non-blocking and -w for timeout. Always lock cron jobs that run longer than their interval.
System cron Jobs
# System cron directories
/etc/cron.d/ # Package-provided cron jobs
/etc/cron.daily/ # Scripts run once daily
/etc/cron.hourly/ # Scripts run once hourly
/etc/cron.weekly/ # Scripts run once weekly
/etc/cron.monthly/ # Scripts run once monthly
# /etc/crontab (system-wide, has USER field)
# minute hour day month weekday user command
0 3 * * * root /usr/bin/backup.sh
# Anacron (for systems not running 24/7)
cat /etc/anacrontab
# 1 5 cron.daily nice run-parts /etc/cron.daily
# 7 25 cron.weekly nice run-parts /etc/cron.weekly
# @monthly 45 cron.monthly nice run-parts /etc/cron.monthly
# delay period identifier commandAnacron ensures daily/weekly/monthly jobs run even if the system was off when they were scheduled. It runs them at next boot with a delay.
at One-Shot Jobs
# Schedule a job for later
echo "/usr/bin/backup.sh" | at 2:00 AM
echo "reboot" | at now + 30 minutes
# Interactive at prompt
at now + 1 hour
# at> /usr/bin/backup.sh
# at> Ctrl+D
# List pending jobs
atq
# 1234 2024-01-15 02:00 a root
# View job details
at -c 1234
# Delete a job
atrm 1234
# at.allow and at.deny
# /etc/at.allow — users allowed to use at
# /etc/at.deny — users denied from using at
# Batch mode (runs when system load is low)
echo "/usr/bin/heavy-task.sh" | batchat is for one-time scheduling. For recurring tasks, use cron or systemd timers. at jobs are lost on reboot unless configured otherwise.
Alternatives to cron
| Tool | Best For | Features |
|---|---|---|
| systemd timers | System services | Logging, dependencies, resource control |
| cron | Simple recurring tasks | Universal, simple syntax |
| at | One-shot scheduled tasks | Simple, runs once |
| anacron | Laptops, non-24/7 systems | Missed job recovery |
systemd timers provide better logging, dependency management, and resource control than cron. Use them for all new scheduled tasks.
Cron sends stdout/stderr via email. Redirect output to a log file to avoid filling the mail queue and to enable debugging.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.