google-site-verification: google61fe8ba583a51912.html
Complete Guide to Bash crontab Command (Schedule Tasks)

Introduction to crontab

The crontab command is used to schedule tasks (cron jobs) to run automatically at specified times or intervals in Unix/Linux systems. It's an essential tool for automating repetitive tasks like backups, system maintenance, log rotation, and monitoring.

Key Concepts

  • Cron: The daemon that executes scheduled commands
  • Crontab: The file containing cron job definitions
  • Cron Jobs: Individual scheduled commands
  • Schedule Expression: Time specification for when to run jobs
  • Environment: Cron jobs run with limited environment variables

1. Basic Crontab Commands

Managing Crontab Files

# List current user's crontab crontab -l # Edit current user's crontab crontab -e # Remove current user's crontab crontab -r # View another user's crontab (requires root) sudo crontab -u username -l # Edit another user's crontab (requires root) sudo crontab -u username -e # List with line numbers crontab -l | cat -n # Backup crontab crontab -l > my_crontab_backup.txt # Restore from backup crontab my_crontab_backup.txt # Check if crontab exists if crontab -l &>/dev/null; then echo "Crontab exists" else echo "No crontab for user" fi

Crontab File Format

# Each line has 6 fields: # minute hour day month day_of_week command # * * * * * command_to_execute # │ │ │ │ │ # │ │ │ │ └── day of week (0-7, 0 and 7 = Sunday) # │ │ │ └──── month (1-12) # │ │ └────── day of month (1-31) # │ └──────── hour (0-23) # └────────── minute (0-59) # Example: Run every day at 2:30 AM 30 2 * * * /home/user/backup.sh # Run every hour 0 * * * * /usr/bin/php /var/www/html/cron.php # Run every Monday at 5 PM 0 17 * * 1 /home/user/weekly_report.sh # Run on the 1st of every month at midnight 0 0 1 * * /home/user/monthly_cleanup.sh

2. Time Specification Syntax

Basic Time Values

# Numeric values # Minute: 0-59 # Hour: 0-23 # Day of month: 1-31 # Month: 1-12 # Day of week: 0-7 (0 and 7 = Sunday) # Examples 30 4 * * * /script.sh # Daily at 4:30 AM 0 22 * * * /script.sh # Daily at 10:00 PM 15 10 * * * /script.sh # Daily at 10:15 AM 0 0 * * * /script.sh # Daily at midnight

Using Ranges and Lists

# Ranges (using hyphen) 0 9-17 * * * /script.sh # Every hour from 9 AM to 5 PM 30 1-5 * * * /script.sh # At 1:30, 2:30, 3:30, 4:30, 5:30 # Lists (using commas) 15,30,45 * * * * /script.sh # At 15, 30, and 45 minutes past every hour 0 9,13,17 * * * /script.sh # At 9 AM, 1 PM, and 5 PM # Combined ranges and lists 0 9-17/2 * * * /script.sh # Every 2 hours from 9 AM to 5 PM 15,45 9-17 * * * /script.sh # At 9:15, 9:45, 10:15, 10:45, etc. # Multiple ranges 0 9-12,14-17 * * * /script.sh # Every hour from 9-12 and 14-17

Using Steps

# Steps (using /) */15 * * * * /script.sh # Every 15 minutes 0 */2 * * * /script.sh # Every 2 hours 0 0 */3 * * /script.sh # Every 3 days */10 9-17 * * * /script.sh # Every 10 minutes between 9 AM and 5 PM # Common step examples */5 * * * * /script.sh # Every 5 minutes */10 * * * * /script.sh # Every 10 minutes */30 * * * * /script.sh # Every 30 minutes 0 */6 * * * /script.sh # Every 6 hours 0 0 */2 * * /script.sh # Every 2 days

Special Keywords

# Special time specifications @reboot # Run once at startup @yearly # Run once a year (0 0 1 1 *) @annually # Same as @yearly @monthly # Run once a month (0 0 1 * *) @weekly # Run once a week (0 0 * * 0) @daily # Run once a day (0 0 * * *) @midnight # Same as @daily @hourly # Run once an hour (0 * * * *) # Examples @reboot /home/user/startup.sh @daily /home/user/daily_backup.sh @weekly /home/user/weekly_report.sh @monthly /home/user/monthly_cleanup.sh @yearly /home/user/yearly_archive.sh # Note: These may not be available on all systems # For maximum compatibility, use numeric equivalents: # @daily -> 0 0 * * * # @hourly -> 0 * * * *

3. Practical Cron Job Examples

System Maintenance Jobs

# Edit crontab crontab -e # Daily system update (at 2 AM) 0 2 * * * /usr/bin/apt-get update && /usr/bin/apt-get upgrade -y >> /var/log/update.log 2>&1 # Weekly disk cleanup (Sunday at 3 AM) 0 3 * * 0 /home/user/scripts/cleanup.sh # Monthly log rotation (1st of month at 4 AM) 0 4 1 * * /usr/sbin/logrotate /etc/logrotate.conf # Check disk space every hour and alert if low 0 * * * * /home/user/scripts/check_disk.sh # Backup database daily at 1 AM 0 1 * * * /usr/bin/mysqldump -u root mydb > /backup/mydb_$(date +\%Y\%m\%d).sql # Remove old backups (older than 30 days) daily at 2 AM 0 2 * * * find /backup -type f -name "*.sql" -mtime +30 -delete

Backup Script with Crontab

#!/bin/bash # backup.sh - Comprehensive backup script # Configuration BACKUP_DIR="/backup" LOG_FILE="/var/log/backup.log" DATABASES=("db1" "db2" "db3") FILES=("/home/user/documents" "/home/user/pictures") # Logging function log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" } # Create backup directory if not exists mkdir -p "$BACKUP_DIR" # Database backups for db in "${DATABASES[@]}"; do log "Starting database backup: $db" if mysqldump -u backup "$db" | gzip > "$BACKUP_DIR/${db}_$(date +\%Y\%m\%d).sql.gz"; then log "Database backup successful: $db" else log "Database backup failed: $db" fi done # File backups for dir in "${FILES[@]}"; do name=$(basename "$dir") log "Starting file backup: $name" if tar -czf "$BACKUP_DIR/${name}_$(date +\%Y\%m\%d).tar.gz" "$dir" 2>/dev/null; then log "File backup successful: $name" else log "File backup failed: $name" fi done # Clean old backups (keep 30 days) find "$BACKUP_DIR" -type f -name "*.gz" -mtime +30 -delete log "Backup process completed"

Crontab Entries for the Script

# Database backup every 6 hours 0 */6 * * * /home/user/scripts/backup.sh # Daily backup at 1 AM 0 1 * * * /home/user/scripts/backup.sh >> /var/log/backup_cron.log 2>&1 # Weekly full backup (Sunday at 2 AM) 0 2 * * 0 /home/user/scripts/full_backup.sh # Monthly archive (1st of month at 3 AM) 0 3 1 * * /home/user/scripts/archive_monthly.sh

Monitoring Script

#!/bin/bash # monitor.sh - System monitoring script # Configuration THRESHOLD_CPU=80 THRESHOLD_MEM=90 THRESHOLD_DISK=85 ALERT_EMAIL="[email protected]" LOG_FILE="/var/log/monitor.log" # Get current timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Check CPU load load=$(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1 | xargs) cores=$(nproc) cpu_percent=$(echo "$load * 100 / $cores" | bc) if (( $(echo "$cpu_percent > $THRESHOLD_CPU" | bc -l) )); then message="High CPU usage: ${cpu_percent}% (threshold: ${THRESHOLD_CPU}%)" echo "$timestamp - $message" >> "$LOG_FILE" echo "$message" | mail -s "CPU Alert" "$ALERT_EMAIL" fi # Check memory usage mem_used=$(free | awk '/Mem:/ {print int($3/$2 * 100)}') if [ "$mem_used" -gt "$THRESHOLD_MEM" ]; then message="High memory usage: ${mem_used}% (threshold: ${THRESHOLD_MEM}%)" echo "$timestamp - $message" >> "$LOG_FILE" echo "$message" | mail -s "Memory Alert" "$ALERT_EMAIL" fi # Check disk usage df -h | grep -E '^/dev/' | while read line; do usage=$(echo "$line" | awk '{print $5}' | sed 's/%//') mount=$(echo "$line" | awk '{print $6}') if [ "$usage" -gt "$THRESHOLD_DISK" ]; then message="High disk usage on $mount: ${usage}%" echo "$timestamp - $message" >> "$LOG_FILE" echo "$message" | mail -s "Disk Alert" "$ALERT_EMAIL" fi done # Check if critical services are running services=("ssh" "cron" "nginx") for service in "${services[@]}"; do if ! systemctl is-active --quiet "$service"; then message="Service $service is not running" echo "$timestamp - $message" >> "$LOG_FILE" echo "$message" | mail -s "Service Alert" "$ALERT_EMAIL" # Attempt to restart systemctl start "$service" if [ $? -eq 0 ]; then echo "$timestamp - Restarted $service successfully" >> "$LOG_FILE" fi fi done

Crontab for Monitoring

# Monitor every 5 minutes */5 * * * * /home/user/scripts/monitor.sh # Check disk space every hour 0 * * * * /home/user/scripts/check_disk.sh # Monitor services every minute * * * * * /home/user/scripts/check_services.sh # Generate daily report at 11:55 PM 55 23 * * * /home/user/scripts/daily_report.sh

4. Environment and Output Handling

Setting Environment Variables

# Set environment variables in crontab SHELL=/bin/bash PATH=/usr/local/bin:/usr/bin:/bin [email protected] HOME=/home/user LOG_DIR=/var/log/myapp # Use variables in commands 0 2 * * * $HOME/scripts/backup.sh >> $LOG_DIR/backup.log 2>&1 # Example crontab with environment SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin [email protected] HOME=/home/user # Daily backup with logging 0 2 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1 # Weekly report 0 9 * * 1 /home/user/scripts/weekly_report.sh | mail -s "Weekly Report" [email protected]

Handling Output and Errors

# Redirect stdout and stderr to file 0 2 * * * /home/user/script.sh >> /var/log/script.log 2>&1 # Different files for stdout and stderr 0 2 * * * /home/user/script.sh >> /var/log/script.out 2>> /var/log/script.err # Suppress all output 0 2 * * * /home/user/script.sh > /dev/null 2>&1 # Send output by email (default if MAILTO is set) [email protected] 0 2 * * * /home/user/script.sh # Pipe output to another command 0 2 * * * /home/user/script.sh | logger -t myscript # Append timestamp to log 0 2 * * * echo "$(date): Starting backup" >> /var/log/backup.log && /home/user/backup.sh >> /var/log/backup.log 2>&1 # Rotate log files 0 0 * * * mv /var/log/script.log /var/log/script.log.$(date +\%Y\%m\%d) && touch /var/log/script.log

Using Logger for Syslog

# Log to syslog */5 * * * * /home/user/script.sh | logger -t myscript -p user.info # Log with different priorities 0 * * * * echo "Hourly check" | logger -t cron -p cron.info 0 2 * * * /home/user/backup.sh || logger -t cron -p cron.err "Backup failed" # View cron logs grep CRON /var/log/syslog journalctl -u cron -f

5. Advanced Crontab Techniques

Multiple Commands in One Line

# Run multiple commands (separate with && or ;) 0 2 * * * cd /home/user && ./backup.sh && echo "Backup complete" >> /var/log/backup.log # Run commands sequentially regardless of success 0 2 * * * command1; command2; command3 # Run only if previous succeeded 0 2 * * * command1 && command2 && command3 # Conditional execution with if 0 2 * * * if [ -f /tmp/lock ]; then exit; fi; /home/user/script.sh # Complex command chains 0 2 * * * /home/user/script.sh || /home/user/fallback.sh | mail -s "Alert" [email protected]

Using Conditional Execution

# Run only on weekdays 0 2 * * 1-5 /home/user/script.sh # Run only on weekends 0 2 * * 0,6 /home/user/weekend_script.sh # Run only if file exists 0 2 * * * [ -f /tmp/run ] && /home/user/script.sh # Run only if not already running 0 2 * * * /usr/bin/flock -n /tmp/myscript.lock /home/user/script.sh # Run only if load average is low 0 2 * * * [ $(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1 | cut -d. -f1) -lt 5 ] && /home/user/script.sh # Check network connectivity before running 0 2 * * * ping -c 1 google.com &>/dev/null && /home/user/online_script.sh || /home/user/offline_script.sh

Preventing Overlapping Runs

# Using flock to prevent overlapping */5 * * * * /usr/bin/flock -n /tmp/myapp.lock /home/user/script.sh # Create PID file check 0 2 * * * /home/user/script_with_pid.sh # script_with_pid.sh contents: #!/bin/bash PIDFILE=/tmp/myscript.pid if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then echo "Script already running" exit 1 fi echo $$ > "$PIDFILE" # ... main script ... rm "$PIDFILE" # Using mkdir as atomic operation 0 2 * * * /home/user/script_with_lockdir.sh # script_with_lockdir.sh: #!/bin/bash LOCKDIR=/tmp/myscript.lock if mkdir "$LOCKDIR" 2>/dev/null; then trap "rm -rf '$LOCKDIR'" EXIT # ... main script ... else echo "Script already running" exit 1 fi

Dynamic Time Specifications

# Run at random minute to avoid contention RANDOM_MINUTE=$((RANDOM % 60)) echo "$RANDOM_MINUTE 2 * * * /home/user/script.sh" | crontab - # Run at calculated times # Calculate next run time in script and schedule #!/bin/bash # dynamic_scheduler.sh next_run=$(date -d "tomorrow 2am" +%M %H %d %m %w) echo "$next_run /home/user/script.sh" | crontab - # Use system load to determine if should run 0 2 * * * /home/user/conditional_runner.sh # conditional_runner.sh: #!/bin/bash load=$(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1) if (( $(echo "$load < 2" | bc -l) )); then /home/user/main_script.sh else echo "Load too high, skipping run" >> /var/log/skipped.log fi

6. Managing Multiple Crontabs

User-Specific Crontabs

# Each user has their own crontab crontab -u user1 -l crontab -u user2 -e crontab -u user3 -r # System-wide crontab (/etc/crontab) sudo cat /etc/crontab # Format for /etc/crontab includes user field: # minute hour day month day-of-week user command 0 2 * * * root /usr/bin/backup.sh # Drop-in directories /etc/cron.d/ # System cron files /etc/cron.hourly/ # Scripts run hourly /etc/cron.daily/ # Scripts run daily /etc/cron.weekly/ # Scripts run weekly /etc/cron.monthly/ # Scripts run monthly

System Cron Directories

# Place scripts in these directories to run at intervals # Hourly scripts sudo cp myscript.sh /etc/cron.hourly/ sudo chmod +x /etc/cron.hourly/myscript.sh # Daily scripts sudo cp backup.sh /etc/cron.daily/ sudo chmod +x /etc/cron.daily/backup.sh # Weekly scripts sudo cp report.sh /etc/cron.weekly/ sudo chmod +x /etc/cron.weekly/report.sh # Monthly scripts sudo cp cleanup.sh /etc/cron.monthly/ sudo chmod +x /etc/cron.monthly/cleanup.sh # Note: Scripts in these directories should: # 1. Not require terminal input # 2. Handle errors gracefully # 3. Log output appropriately # 4. Be idempotent

Crontab Management Script

#!/bin/bash # manage_crontab.sh - Manage multiple crontab configurations CRONTAB_DIR="/home/user/crontabs" mkdir -p "$CRONTAB_DIR" # Function to backup current crontab backup_crontab() { local user="${1:-$USER}" local backup_file="$CRONTAB_DIR/${user}_crontab_$(date +%Y%m%d_%H%M%S).txt" if crontab -u "$user" -l &>/dev/null; then crontab -u "$user" -l > "$backup_file" echo "Backed up crontab for $user to $backup_file" else echo "No crontab for $user" fi } # Function to load crontab from file load_crontab() { local file="$1" local user="${2:-$USER}" if [ ! -f "$file" ]; then echo "File not found: $file" return 1 fi crontab -u "$user" "$file" echo "Loaded crontab for $user from $file" } # Function to list all user crontabs list_all_crontabs() { echo "=== User Crontabs ===" for user in $(cut -d: -f1 /etc/passwd); do if crontab -u "$user" -l &>/dev/null; then echo "--- $user ---" crontab -u "$user" -l | head -5 echo "..." fi done } # Function to enable/disable cron jobs toggle_job() { local pattern="$1" local action="${2:-disable}" # disable or enable crontab -l | while read line; do if [[ "$line" =~ $pattern && "$action" == "disable" ]]; then echo "# DISABLED: $line" elif [[ "$line" =~ ^#.*DISABLED.*$pattern && "$action" == "enable" ]]; then echo "${line#*DISABLED: }" else echo "$line" fi done | crontab - echo "Jobs matching '$pattern' ${action}d" } # Function to validate crontab syntax validate_crontab() { local file="${1:-/dev/stdin}" if crontab -l 2>/dev/null | crontab - 2>&1; then echo "✓ Crontab syntax is valid" else echo "✗ Crontab syntax error" fi } # Function to show next run times next_runs() { local count="${1:-5}" crontab -l | grep -v '^#' | while read line; do if [ -n "$line" ]; then echo "Next run for: $line" echo " $(echo "$line" | awk '{print $1" "$2" "$3" "$4" "$5}')" fi done } # Menu case "${1:-}" in backup) backup_crontab "${2:-$USER}" ;; load) load_crontab "$2" "${3:-$USER}" ;; list) list_all_crontabs ;; toggle) toggle_job "$2" "${3:-disable}" ;; validate) validate_crontab ;; next) next_runs "${2:-5}" ;; *) echo "Usage: $0 {backup|load|list|toggle|validate|next}" ;; esac

7. Cron Job Best Practices

Script Best Practices

#!/bin/bash # Example of a well-behaved cron script # 1. Use full paths BACKUP_SCRIPT="/home/user/scripts/backup.sh" LOG_FILE="/var/log/backup.log" # 2. Set environment export PATH=/usr/local/bin:/usr/bin:/bin export HOME=/home/user # 3. Use locking to prevent overlap LOCKFILE="/tmp/$(basename "$0").lock" if [ -f "$LOCKFILE" ] && kill -0 $(cat "$LOCKFILE") 2>/dev/null; then echo "$(date): Script already running" >> "$LOG_FILE" exit 1 fi echo $$ > "$LOCKFILE" trap "rm -f '$LOCKFILE'" EXIT # 4. Handle errors gracefully error_handler() { echo "$(date): Error on line $1" >> "$LOG_FILE" exit 1 } trap 'error_handler $LINENO' ERR # 5. Log everything log() { echo "$(date): $1" >> "$LOG_FILE" } # 6. Check prerequisites check_prereqs() { for cmd in mysqldump gzip tar; do if ! command -v "$cmd" &>/dev/null; then log "ERROR: $cmd not found" exit 1 fi done } # 7. Main script logic main() { log "Starting backup" # Your backup logic here if /usr/bin/mysqldump -u root mydb | gzip > "/backup/mydb_$(date +%Y%m%d).sql.gz"; then log "Backup successful" else log "Backup failed" exit 1 fi log "Backup completed" } # 8. Run main function check_prereqs main

Logging and Monitoring

# Create a logging wrapper for cron jobs #!/bin/bash # cron_wrapper.sh - Wrapper script for cron jobs JOB_NAME=$(basename "$1") LOG_DIR="/var/log/cron" LOG_FILE="$LOG_DIR/${JOB_NAME}_$(date +%Y%m%d).log" PID_FILE="/tmp/${JOB_NAME}.pid" # Create log directory mkdir -p "$LOG_DIR" # Log function log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" } # Check if already running if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then log "Job already running with PID $(cat "$PID_FILE")" exit 1 fi echo $$ > "$PID_FILE" trap "rm -f '$PID_FILE'" EXIT log "Starting job: $@" # Execute the actual job "$@" >> "$LOG_FILE" 2>&1 JOB_EXIT=$? if [ $JOB_EXIT -eq 0 ]; then log "Job completed successfully" else log "Job failed with exit code $JOB_EXIT" # Send alert on failure if [ -n "$ALERT_EMAIL" ]; then tail -50 "$LOG_FILE" | mail -s "Cron job failed: $JOB_NAME" "$ALERT_EMAIL" fi fi exit $JOB_EXIT

Error Handling and Notifications

#!/bin/bash # error_handler.sh - Robust error handling for cron jobs # Configuration SCRIPT_NAME=$(basename "$0") LOG_FILE="/var/log/${SCRIPT_NAME%.sh}.log" ALERT_EMAIL="[email protected]" MAX_RETRIES=3 RETRY_DELAY=60 # Logging log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" echo "$1" } # Error handling handle_error() { local exit_code=$1 local error_msg=$2 log "ERROR: $error_msg (exit code: $exit_code)" # Send alert email { echo "Cron job failed: $SCRIPT_NAME" echo "Exit code: $exit_code" echo "Error: $error_msg" echo "Time: $(date)" echo "" echo "Last 50 lines of log:" tail -50 "$LOG_FILE" } | mail -s "Cron Alert: $SCRIPT_NAME" "$ALERT_EMAIL" # Optional: notify via other channels # curl -X POST https://api.telegram.org/bot$TOKEN/sendMessage -d "chat_id=$CHAT_ID&text=$error_msg" } # Retry function retry() { local n=1 local cmd="$@" until eval "$cmd"; do log "Command failed (attempt $n/$MAX_RETRIES): $cmd" if [ $n -lt $MAX_RETRIES ]; then log "Retrying in $RETRY_DELAY seconds..." sleep $RETRY_DELAY ((n++)) else handle_error $? "Command failed after $MAX_RETRIES attempts: $cmd" return 1 fi done log "Command succeeded: $cmd" return 0 } # Main execution with error handling main() { log "Starting $SCRIPT_NAME" # Check dependencies for cmd in curl wget mysql; do if ! command -v "$cmd" &>/dev/null; then handle_error 1 "Required command not found: $cmd" exit 1 fi done # Example: retry a database backup if retry mysqldump -u backup mydb > /tmp/backup.sql; then log "Database backup successful" else log "Database backup failed after retries" exit 1 fi # Example: upload with retry if retry curl -T /tmp/backup.sql ftp://backup-server/; then log "Upload successful" else log "Upload failed after retries" exit 1 fi log "$SCRIPT_NAME completed successfully" } # Run main with trap trap 'handle_error $? "Script interrupted"' INT TERM main

8. Testing and Debugging Cron Jobs

Testing Cron Syntax

# Check crontab syntax crontab -l | crontab - 2>&1 # Test run a cron job manually /home/user/scripts/test.sh # Simulate cron environment env -i SHELL=/bin/bash PATH=/usr/local/bin:/usr/bin:/bin HOME=/home/user /home/user/script.sh # Test with specific time # Use date command to set time for testing sudo date -s "2024-01-01 02:00:00" # Run job # Set date back sudo ntpdate -u pool.ntp.org # Use run-parts to test system cron directories run-parts --test /etc/cron.daily

Debugging Tools

# Check cron logs grep CRON /var/log/syslog tail -f /var/log/syslog | grep CRON # Check if cron service is running systemctl status cron service cron status ps aux | grep cron # Check mail for cron output mail # View mail for specific user sudo cat /var/mail/$USER # Test individual commands # Run with full logging /home/user/script.sh >> /tmp/debug.log 2>&1 # Use logger for debugging echo "Debug: Starting job" | logger -t cron-debug # Check environment variables env | sort > /tmp/env_before.txt # Run job env | sort > /tmp/env_after.txt diff /tmp/env_before.txt /tmp/env_after.txt

Debugging Script

#!/bin/bash # debug_cron.sh - Debug cron job issues CRON_LOG="/var/log/syslog" USER=${1:-$USER} echo "=== Cron Debug Tool ===" echo "User: $USER" echo # Check if cron is running echo "1. Cron service status:" if systemctl is-active --quiet cron 2>/dev/null; then echo " ✓ Cron is running" else echo " ✗ Cron is NOT running" fi echo # Check user's crontab echo "2. User crontab:" if crontab -l &>/dev/null; then crontab -l | cat -n else echo " No crontab for user" fi echo # Check recent cron activity echo "3. Recent cron activity (last 10 entries):" sudo grep "CRON" "$CRON_LOG" 2>/dev/null | tail -10 echo # Check for errors echo "4. Recent cron errors:" sudo grep -i "error\|fail" "$CRON_LOG" 2>/dev/null | grep CRON | tail -5 echo # Check mail echo "5. Pending mail:" if [ -f "/var/mail/$USER" ]; then ls -l "/var/mail/$USER" echo " Mail size: $(du -h /var/mail/$USER | cut -f1)" else echo " No mail file" fi echo # Test environment echo "6. Cron environment simulation:" env -i SHELL=/bin/bash PATH=/usr/local/bin:/usr/bin:/bin HOME="/home/$USER" bash -c 'echo " PATH=$PATH"' echo # Test execute permissions echo "7. Checking script permissions:" crontab -l | grep -v '^#' | grep -E '^[^ ]' | while read line; do cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i}') script=$(echo "$cmd" | awk '{print $1}') if [ -f "$script" ]; then if [ -x "$script" ]; then echo " ✓ $script is executable" else echo " ✗ $script is NOT executable" fi else echo " ? $script not found" fi done

9. Security Considerations

Securing Crontab Access

# Restrict crontab access to specific users # /etc/cron.allow - users allowed to use crontab # /etc/cron.deny - users denied from using crontab # Example: Only allow root and backup user echo "root" >> /etc/cron.allow echo "backup" >> /etc/cron.allow echo "ALL" >> /etc/cron.deny # Deny all others # Secure permissions chmod 600 /etc/cron.allow chmod 600 /etc/cron.deny chmod 600 /var/spool/cron/* # Protect crontab files # Run cron jobs with minimal privileges # Create dedicated user for cron jobs sudo useradd -r -s /bin/false cronjobs sudo -u cronjobs crontab -e # Use sudo for specific tasks # In crontab: 0 2 * * * sudo -u backup /usr/local/bin/backup.sh

Safe Practices

# 1. Don't run as root unless necessary # Bad 0 2 * * * /home/user/script.sh # Good 0 2 * * * sudo -u user /home/user/script.sh # 2. Use absolute paths # Bad 0 2 * * * backup.sh # Good 0 2 * * * /home/user/scripts/backup.sh # 3. Validate input in scripts #!/bin/bash # Safe script example if [ -z "$1" ]; then echo "Usage: $0 <argument>" exit 1 fi # 4. Avoid using environment variables that could be manipulated # Bad 0 2 * * * $HOME/script.sh # Good 0 2 * * * /home/user/script.sh # 5. Use locking to prevent resource exhaustion 0 2 * * * /usr/bin/flock -n /tmp/myscript.lock /home/user/script.sh # 6. Log all actions 0 2 * * * /home/user/script.sh >> /var/log/script.log 2>&1 # 7. Set umask for secure file creation 0 2 * * * umask 077 && /home/user/script.sh # 8. Monitor for unusual cron jobs find /var/spool/cron -type f -exec ls -l {} \; grep -r "curl\|wget\|nc\|bash -i" /var/spool/cron/

10. Advanced Examples

Complex Scheduling Patterns

# Run on specific days of month # 1st and 15th at 2 AM 0 2 1,15 * * /home/user/script.sh # Run on last day of month (using date command) 55 23 28-31 * * [ $(date -d tomorrow +%d) -eq 1 ] && /home/user/script.sh # Run every weekday except holidays 0 9 * * 1-5 [ ! -f /etc/holidays/$(date +%Y%m%d) ] && /home/user/script.sh # Run at specific times with offset # Every 2 hours, starting at 1 AM 1 */2 * * * /home/user/script.sh # Run every 3 hours between 9 AM and 6 PM 0 9-18/3 * * * /home/user/script.sh # Run twice daily at random minutes RANDOM1=$((RANDOM % 60)) RANDOM2=$((RANDOM % 60)) echo "$RANDOM1 9 * * * /home/user/morning.sh" | crontab - echo "$RANDOM2 17 * * * /home/user/evening.sh" | crontab -

Weather-Aware Scheduling

#!/bin/bash # weather_aware.sh - Run based on weather conditions # Get weather data (example using wttr.in) weather=$(curl -s "wttr.in?format=%t+%w+%p") temp=$(echo "$weather" | awk '{print $1}' | sed 's/[^0-9-]//g') wind=$(echo "$weather" | awk '{print $2}' | sed 's/[^0-9]//g') precip=$(echo "$weather" | awk '{print $3}' | sed 's/[^0-9]//g') LOG_FILE="/var/log/weather_job.log" log() { echo "$(date): $1" >> "$LOG_FILE" } # Decision logic if [ "$precip" -gt 0 ] && [ "$precip" -lt 30 ]; then log "Light rain detected, running job" /home/user/rain_job.sh elif [ "$wind" -gt 20 ]; then log "High winds, delaying job" # Reschedule for later echo "$((RANDOM % 60)) $((RANDOM % 23)) * * * /home/user/weather_aware.sh" | crontab - elif [ "$temp" -lt 0 ]; then log "Freezing temperature, running cold-weather job" /home/user/cold_job.sh else log "Normal conditions, running regular job" /home/user/regular_job.sh fi

Load-Aware Scheduling

#!/bin/bash # load_aware.sh - Run based on system load LOAD_THRESHOLD=2.0 MAX_RETRIES=5 RETRY_DELAY=300 # 5 minutes get_load() { uptime | awk -F'load average:' '{print $2}' | cut -d, -f1 | xargs } run_with_load_check() { local cmd="$@" local attempt=1 while [ $attempt -le $MAX_RETRIES ]; do load=$(get_load) if (( $(echo "$load < $LOAD_THRESHOLD" | bc -l) )); then echo "$(date): Load $load below threshold, running command" eval "$cmd" return $? else echo "$(date): Load $load above threshold, attempt $attempt/$MAX_RETRIES" sleep $RETRY_DELAY ((attempt++)) fi done echo "$(date): Maximum retries reached, command skipped" return 1 } # Usage run_with_load_check /home/user/heavy_job.sh

Chained Jobs with Dependencies

#!/bin/bash # job_chain.sh - Chain multiple jobs with dependencies JOB_DIR="/home/user/jobs" LOG_DIR="/var/log/job_chain" LOCK_DIR="/tmp/job_chain.lock" mkdir -p "$LOG_DIR" "$LOCK_DIR" run_job() { local job="$1" local log="$LOG_DIR/$(basename "$job")_$(date +%Y%m%d).log" echo "$(date): Starting $job" >> "$log" if bash "$job" >> "$log" 2>&1; then echo "$(date): Job $job completed" >> "$log" touch "$LOCK_DIR/$(basename "$job").done" return 0 else echo "$(date): Job $job failed" >> "$log" return 1 fi } # Define job dependencies jobs=( "01_prepare.sh" "02_process.sh:01_prepare.sh.done" "03_analyze.sh:02_process.sh.done" "04_cleanup.sh:03_analyze.sh.done" ) for job_spec in "${jobs[@]}"; do job=$(echo "$job_spec" | cut -d: -f1) dep=$(echo "$job_spec" | cut -d: -f2-) # Check dependency if [ -n "$dep" ] && [ ! -f "$LOCK_DIR/$dep" ]; then echo "Skipping $job, dependency $dep not satisfied" continue fi # Run job with exclusive lock ( flock -n 9 || exit 1 run_job "$JOB_DIR/$job" ) 9>"$LOCK_DIR/$(basename "$job").lock" if [ $? -ne 0 ]; then echo "Job chain failed at $job" exit 1 fi done echo "Job chain completed successfully"

11. Monitoring and Alerting

Cron Job Monitoring System

#!/bin/bash # cron_monitor.sh - Monitor cron jobs and send alerts CONFIG_FILE="/etc/cron_monitor.conf" ALERT_EMAIL="[email protected]" CHECK_INTERVAL=3600 # 1 hour # Load configuration [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE" # Check if a job is running too long check_job_duration() { local job_pattern="$1" local max_minutes="$2" pgrep -f "$job_pattern" | while read pid; do start_time=$(ps -o lstart= -p $pid) start_seconds=$(date -d "$start_time" +%s) now_seconds=$(date +%s) duration=$(( (now_seconds - start_seconds) / 60 )) if [ $duration -gt $max_minutes ]; then echo "Job $job_pattern (PID $pid) running for ${duration} minutes (max: $max_minutes)" return 1 fi done return 0 } # Check if job ran within expected time window check_job_timing() { local job_name="$1" local expected_time="$2" local log_file="$3" last_run=$(grep "$job_name" "$log_file" | tail -1 | cut -d' ' -f1-2) last_seconds=$(date -d "$last_run" +%s 2>/dev/null) now_seconds=$(date +%s) if [ -n "$last_seconds" ]; then diff_hours=$(( (now_seconds - last_seconds) / 3600 )) if [ $diff_hours -gt $expected_time ]; then echo "Job $job_name last ran $diff_hours hours ago (expected within $expected_time hours)" return 1 fi else echo "Job $job_name never ran" return 1 fi return 0 } # Send alert send_alert() { local subject="$1" local message="$2" echo "$message" | mail -s "$subject" "$ALERT_EMAIL" # Optional: Send to Slack if [ -n "$SLACK_WEBHOOK" ]; then curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"$subject\n$message\"}" \ "$SLACK_WEBHOOK" fi # Optional: Send to Telegram if [ -n "$TELEGRAM_BOT" ] && [ -n "$TELEGRAM_CHAT" ]; then curl -s "https://api.telegram.org/bot$TELEGRAM_BOT/sendMessage" \ -d "chat_id=$TELEGRAM_CHAT" \ -d "text=$subject\n$message" fi } # Main monitoring loop while true; do alerts="" # Check each configured job while IFS= read -r line; do [[ "$line" =~ ^#.*$ || -z "$line" ]] && continue job_pattern=$(echo "$line" | cut -d'|' -f1) max_duration=$(echo "$line" | cut -d'|' -f2) expected_interval=$(echo "$line" | cut -d'|' -f3) log_file=$(echo "$line" | cut -d'|' -f4) # Check duration if ! check_job_duration "$job_pattern" "$max_duration"; then alerts+="Job $job_pattern running too long\n" fi # Check timing if [ -n "$expected_interval" ] && [ -n "$log_file" ]; then if ! check_job_timing "$job_pattern" "$expected_interval" "$log_file"; then alerts+="Job $job_pattern not running on schedule\n" fi fi done < "$CONFIG_FILE" # Send alerts if any if [ -n "$alerts" ]; then send_alert "Cron Monitor Alert" "$alerts" fi sleep $CHECK_INTERVAL done

Configuration File Example

# /etc/cron_monitor.conf # Format: job_pattern|max_duration_minutes|expected_interval_hours|log_file # Database backup - should run within 30 minutes, expected every 24 hours mysqldump|30|24|/var/log/backup.log # Log rotation - should complete in 10 minutes, expected every 24 hours logrotate|10|24|/var/log/logrotate.log # System update - can take up to 60 minutes, expected weekly (168 hours) apt-get|60|168|/var/log/update.log # Monitoring job - should run continuously monitor.sh|60||

12. Crontab Cheat Sheet

Quick Reference

# Field Format # ┌──────── minute (0-59) # │ ┌────── hour (0-23) # │ │ ┌──── day of month (1-31) # │ │ │ ┌── month (1-12) # │ │ │ │ ┌ day of week (0-6 or 0-7, 0/7 = Sunday) # │ │ │ │ │ # * * * * * command # Common Examples * * * * * command # Every minute */5 * * * * command # Every 5 minutes 0 * * * * command # Every hour 0 0 * * * command # Daily at midnight 0 2 * * * command # Daily at 2 AM 0 0 * * 0 command # Weekly on Sunday 0 0 1 * * command # Monthly on 1st 0 0 1 1 * command # Yearly on Jan 1st # Ranges and Lists 0 9-17 * * * command # Every hour 9 AM - 5 PM 0 9,13,17 * * * command # At 9 AM, 1 PM, 5 PM 15,30,45 * * * * command # At 15, 30, 45 minutes 0 */2 * * * command # Every 2 hours # Special Keywords @reboot # At startup @yearly # 0 0 1 1 * @monthly # 0 0 1 * * @weekly # 0 0 * * 0 @daily # 0 0 * * * @midnight # 0 0 * * * @hourly # 0 * * * * # Common crontab commands crontab -l # List current crontab crontab -e # Edit current crontab crontab -r # Remove current crontab crontab -u user -l # List user's crontab crontab file.txt # Load crontab from file

Quick Setup Examples

# Daily backup at 2 AM with logging echo "0 2 * * * /home/user/backup.sh >> /var/log/backup.log 2>&1" | crontab - # Multiple jobs cat << EOF | crontab - # Database backup at 1 AM 0 1 * * * /home/user/db_backup.sh # Log rotation at 2 AM 0 2 * * * /home/user/rotate_logs.sh # Weekly report on Monday at 9 AM 0 9 * * 1 /home/user/weekly_report.sh # Monitoring every 5 minutes */5 * * * * /home/user/monitor.sh EOF # Conditional job 0 2 * * * [ $(date +\%u) -le 5 ] && /home/user/weekday_job.sh # Job with flock to prevent overlap */5 * * * * /usr/bin/flock -n /tmp/mylock /home/user/script.sh

Conclusion

Crontab is an essential tool for automating tasks in Unix/Linux systems:

Key Takeaways

  1. Simple Syntax: Five time fields followed by command
  2. Flexible Scheduling: Ranges, lists, steps, and special keywords
  3. User-Specific: Each user can have their own crontab
  4. System-Wide: /etc/crontab and cron directories for system tasks
  5. Environment: Cron provides limited environment, set explicitly
  6. Logging: Always redirect output for debugging
  7. Security: Use least privilege, validate inputs, prevent overlaps

Best Practices Summary

AreaBest Practice
PathsUse absolute paths for all commands and files
LoggingRedirect stdout/stderr to log files
EnvironmentSet PATH and other variables explicitly
TestingTest commands manually before adding to crontab
Error HandlingCheck exit codes and handle failures
LockingPrevent overlapping runs with flock
PermissionsRun jobs with minimal privileges
MonitoringMonitor cron logs and job execution
DocumentationComment complex crontab entries
BackupRegularly backup crontab configurations

Common Issues and Solutions

IssueSolution
Job not runningCheck cron service, syntax, permissions
PATH issuesSet full PATH in crontab
Script failsTest manually with same environment
Overlapping runsUse flock or PID files
No outputCheck MAILTO or redirect to file
Wrong timeVerify system timezone
Permission deniedCheck script executable bits
Command not foundUse full paths

Mastering crontab is essential for system administration and automation. Start with simple schedules and gradually incorporate more complex patterns as needed.

Complete Bash Exercises with Solutions

Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/

Complete Guide to Bash Crontab Command

Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/

Complete Guide to Bash Arrays

Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/

Bash Quiz: Test Your Knowledge

Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/

Complete Guide to Bash Functions

Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/

Complete Guide to Bash Loops

Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/

Complete Guide to Bash If-Else Statements

Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/

Complete Guide to Bash Operators

Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/

Complete Guide to Bash Data Types

Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/

Complete Guide to Bash Variables

Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/

Complete Guide to Bash Scripting

A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/

Complete Guide to Basic Bash Syntax

Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/

Complete Guide to Bash Chgrp Command

Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/

Complete Guide to Bash Chgrp Command (Alternate)

Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/

Complete Guide to Bash File Permissions and Ownership

Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/

Complete Guide to Bash Chmod Command

Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/

Complete Guide to Bash Chown Command

Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/

Complete Guide to Bash SCP Command

Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/

Complete Guide to Bash SSH Command

Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/

Bash Wget Command Complete Guide

Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper