RED HAT ENTERPRISE LINUX

Scheduling Future Tasks

Running Commands at Specific Times with at and cron

College-Level Course Module | RHEL System Administration

Learning Objectives

1
Schedule one-time tasks with at

Run commands at a specific future time using the at command

2
Schedule recurring tasks with cron

Create crontab entries for jobs that run on a schedule

3
Understand crontab syntax

Master the five-field time specification format

4
Manage scheduled jobs

List, modify, and remove scheduled tasks as a regular user

Scheduling Overview

Linux provides tools to run commands automatically at specified times, either once or on a recurring schedule, without user interaction.

at

One-time execution at a specific date/time. Job runs once and is removed.

cron

Recurring execution on a schedule. Jobs repeat until removed.

anacron

Runs missed cron jobs on systems that aren't always on (laptops).

systemd timers

Modern alternative to cron with more features (system-level).

User vs System: Regular users can schedule personal jobs. System-wide jobs in /etc/cron.* require root access.

The at Command

The at command schedules a one-time job to run at a specified time. Commands are read from stdin or a file.

# Schedule a job interactively
[student@server ~]$ at 4:00 PM
warning: commands will be executed using /bin/sh
at> echo "Time to go home!" | mail -s "Reminder" student
at> [Ctrl+D to finish]
job 1 at Fri Jan 19 16:00:00 2024

# Schedule from command line
[student@server ~]$ echo "backup.sh" | at midnight

# Schedule from a file
[student@server ~]$ at 9:00 AM tomorrow -f /home/student/scripts/morning.sh

# Check that atd service is running
[student@server ~]$ systemctl status atd
Requirement: The atd service must be running. Check with systemctl status atd.

at Time Specifications

at 4:00 PM
Today at 4:00 PM (or tomorrow if past)
at 16:00
24-hour format, same as 4:00 PM
at midnight
12:00 AM tonight
at noon
12:00 PM today
at tomorrow
Same time tomorrow
at now + 2 hours
2 hours from now
at now + 30 minutes
30 minutes from now
at 9:00 AM Jan 20
Specific date and time
at 9:00 AM 1/20/24
Date in MM/DD/YY format
at teatime
4:00 PM (a fun alias!)
# Relative time examples
[student@server ~]$ at now + 1 week
[student@server ~]$ at 5 PM + 3 days
[student@server ~]$ at 10:00 AM next Monday

Managing at Jobs

# List pending at jobs
[student@server ~]$ atq
1       Fri Jan 19 16:00:00 2024 a student
2       Sat Jan 20 00:00:00 2024 a student
3       Mon Jan 22 09:00:00 2024 a student

# View contents of a specific job
[student@server ~]$ at -c 1
#!/bin/sh
# atrun uid=1000 gid=1000
...
cd /home/student || { ... }
echo "Time to go home!" | mail -s "Reminder" student

# Remove a job by number
[student@server ~]$ atrm 2
[student@server ~]$ atq
1       Fri Jan 19 16:00:00 2024 a student
3       Mon Jan 22 09:00:00 2024 a student

# Alternative: at -r (same as atrm)
[student@server ~]$ at -r 3
Job Numbers: Each at job gets a unique number. Use this number with at -c to view or atrm to remove.

at Access Control

/etc/at.allow

If exists, only users listed can use at. One username per line.

/etc/at.deny

If at.allow doesn't exist, users listed here are denied. Empty = all allowed.

# Check if you can use at
[student@server ~]$ at now + 1 minute
warning: commands will be executed using /bin/sh
at>    # If you get a prompt, you're allowed

# If denied:
You do not have permission to use at.

# View access files (as root)
[root@server ~]# cat /etc/at.deny
(empty by default - all users allowed)
Default Behavior: By default, /etc/at.deny exists but is empty, allowing all users to use at. If /etc/at.allow exists, it takes precedence.

Introduction to cron

cron is a daemon that executes scheduled commands at recurring intervals. Jobs are defined in crontab (cron table) files.

User Crontabs

Personal schedules managed with crontab -e. Each user has their own.

System Crontabs

/etc/crontab and /etc/cron.d/* for system-wide jobs (root only).

Cron Directories

/etc/cron.hourly, daily, weekly, monthly for simple scheduling.

crond Service

The daemon that reads crontabs and executes jobs on schedule.

# Verify cron daemon is running
[student@server ~]$ systemctl status crond
● crond.service - Command Scheduler
     Loaded: loaded
     Active: active (running)

crontab Commands

# Edit your crontab (opens in default editor)
[student@server ~]$ crontab -e

# List your crontab entries
[student@server ~]$ crontab -l
30 8 * * 1-5 /home/student/scripts/morning-report.sh
0 0 * * * /home/student/scripts/daily-backup.sh

# Remove your entire crontab (dangerous!)
[student@server ~]$ crontab -r

# Remove with confirmation prompt
[student@server ~]$ crontab -ri
crontab: really delete student's crontab? y

# Root can manage other users' crontabs
[root@server ~]# crontab -l -u student
[root@server ~]# crontab -e -u student
⚠ Warning: crontab -r removes ALL your cron jobs without confirmation! Use crontab -ri or just crontab -e to delete specific lines.

Crontab Syntax

30
Minute
(0-59)
8
Hour
(0-23)
*
Day of Month
(1-31)
*
Month
(1-12)
1-5
Day of Week
(0-7)
/path/to/script.sh
Command
# Format: MIN HOUR DOM MON DOW COMMAND

30 8 * * 1-5 /home/student/scripts/morning-report.sh
#  │ │ │ │ └── Day of week: 1-5 (Mon-Fri)
#  │ │ │ └──── Month: * (every month)
#  │ │ └────── Day of month: * (every day)
#  │ └──────── Hour: 8 (8 AM)
#  └────────── Minute: 30

# This job runs at 8:30 AM, Monday through Friday

Crontab Special Characters

CharacterMeaningExample
*Every value* * * * * = every minute
,List of values0,15,30,45 = these minutes
-Range of values9-17 = hours 9 through 17
/Step values*/15 = every 15 units
# Every 15 minutes
*/15 * * * * /path/to/script.sh

# At minutes 0, 15, 30, 45 (same as above)
0,15,30,45 * * * * /path/to/script.sh

# Every weekday at 9 AM
0 9 * * 1-5 /path/to/script.sh

# First day of each month at midnight
0 0 1 * * /path/to/script.sh

# Every 2 hours on weekends
0 */2 * * 0,6 /path/to/script.sh

Crontab Examples

# Every minute (good for testing, remove after!)
* * * * * echo "test" >> /tmp/crontest.log

# Daily at 2:30 AM
30 2 * * * /home/student/scripts/daily-backup.sh

# Every Monday at 9 AM
0 9 * * 1 /home/student/scripts/weekly-report.sh

# First of each month at midnight
0 0 1 * * /home/student/scripts/monthly-cleanup.sh

# Every 5 minutes during business hours on weekdays
*/5 9-17 * * 1-5 /home/student/scripts/check-status.sh

# Twice daily at 8 AM and 8 PM
0 8,20 * * * /home/student/scripts/sync-data.sh

# 15th and last day of month (approximated with 28th)
0 0 15,28 * * /home/student/scripts/bimonthly.sh
Testing Tip: Use * * * * * to run every minute while testing, then change to the real schedule once verified.

Cron Special Strings

Instead of five fields, you can use these shortcuts for common schedules:

StringMeaningEquivalent
@rebootRun once at startupN/A
@yearly / @annuallyOnce a year0 0 1 1 *
@monthlyOnce a month0 0 1 * *
@weeklyOnce a week0 0 * * 0
@daily / @midnightOnce a day0 0 * * *
@hourlyOnce an hour0 * * * *
# Start a process at system boot
@reboot /home/student/scripts/start-app.sh

# Daily backup at midnight
@daily /home/student/scripts/backup.sh

# Weekly cleanup on Sunday at midnight
@weekly /home/student/scripts/cleanup.sh

Cron Environment

Important: Cron jobs run with a minimal environment, not your normal login environment! Many commands may not work as expected.
# Cron's default PATH is limited:
PATH=/usr/bin:/bin

# Set your own PATH and variables in crontab:
PATH=/usr/local/bin:/usr/bin:/bin:/home/student/bin
MAILTO=student@example.com
HOME=/home/student

# Then your jobs:
30 8 * * * morning-report.sh     # Now finds scripts in your bin

# Or use full paths in commands:
30 8 * * * /home/student/scripts/morning-report.sh

# Or source your profile in the script:
#!/bin/bash
source ~/.bash_profile
# rest of script...
MAILTO: Set MAILTO=your@email.com to receive job output. Set MAILTO="" to disable mail.

Cron Output and Logging

# By default, all output is mailed to the user
# Redirect output to a log file instead:
30 2 * * * /path/to/backup.sh >> /home/student/logs/backup.log 2>&1

# Discard all output:
*/5 * * * * /path/to/check.sh > /dev/null 2>&1

# Log with timestamp:
0 * * * * echo "$(date): Starting hourly job" >> ~/logs/cron.log

# In your crontab, disable mail globally:
MAILTO=""
* * * * * /path/to/job.sh

# Check system cron log for execution records:
[student@server ~]$ grep CRON /var/log/cron
Jan 19 08:30:01 server CRON[1234]: (student) CMD (/home/student/scripts/job.sh)
Best Practice: Always redirect output to log files. Include timestamps. Don't rely on cron mail for production jobs.

Cron Access Control

/etc/cron.allow

If exists, only listed users can use cron.

/etc/cron.deny

If cron.allow doesn't exist, listed users are denied.

# Check if you can use cron
[student@server ~]$ crontab -e
# If you can edit, you're allowed

# If denied:
You (student) are not allowed to use this program (crontab)

# View access files (as root)
[root@server ~]# cat /etc/cron.allow
root
admin
student

# Default on RHEL: /etc/cron.deny exists but is empty
# This means all users can use cron

System Cron Directories

# Drop-in directories for simple scheduling (root only)
/etc/cron.hourly/     # Scripts run every hour
/etc/cron.daily/      # Scripts run daily
/etc/cron.weekly/     # Scripts run weekly
/etc/cron.monthly/    # Scripts run monthly

# List daily cron scripts
[student@server ~]$ ls /etc/cron.daily/
logrotate  man-db  mlocate

# System crontab (different format - has username field)
[student@server ~]$ cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# MIN HOUR DOM MON DOW USER  COMMAND
  *   *    *   *   *  root  /path/to/script.sh

# Additional system cron jobs
/etc/cron.d/         # Drop-in crontab files
Note: These system directories require root to modify. For user jobs, use crontab -e instead.

Practical Example: Backup

# Create a backup script
[student@server ~]$ vim ~/scripts/backup-home.sh
#!/bin/bash
# backup-home.sh - Backup home directory
BACKUP_DIR="/home/student/backups"
DATE=$(date +%Y%m%d)
LOG="$BACKUP_DIR/backup.log"

echo "$(date): Starting backup" >> "$LOG"

tar -czf "$BACKUP_DIR/home-$DATE.tar.gz" \
    --exclude="$BACKUP_DIR" \
    /home/student 2>> "$LOG"

if [ $? -eq 0 ]; then
    echo "$(date): Backup completed successfully" >> "$LOG"
else
    echo "$(date): Backup FAILED" >> "$LOG"
fi

# Keep only last 7 backups
ls -t "$BACKUP_DIR"/home-*.tar.gz | tail -n +8 | xargs rm -f 2>/dev/null
# Schedule daily at 2 AM
[student@server ~]$ crontab -e
0 2 * * * /home/student/scripts/backup-home.sh

Troubleshooting

# Check if cron/at daemons are running
[student@server ~]$ systemctl status crond atd

# Check cron log for execution
[student@server ~]$ grep CRON /var/log/cron
[student@server ~]$ journalctl -u crond --since "1 hour ago"

# Test your script manually first!
[student@server ~]$ /home/student/scripts/myscript.sh

# Test with cron's environment
[student@server ~]$ env -i /bin/sh -c '/home/student/scripts/myscript.sh'

# Check file permissions
[student@server ~]$ ls -l /home/student/scripts/myscript.sh
-rwxr-xr-x. 1 student student 256 Jan 19 10:00 myscript.sh

# Verify crontab syntax
[student@server ~]$ crontab -l
Common Problems: Script not executable, wrong PATH, missing dependencies, permission denied, output lost because no redirection.

Best Practices

✓ Do

  • Use absolute paths in scripts and crontab
  • Set PATH and MAILTO in crontab
  • Redirect output to log files
  • Include timestamps in logs
  • Test scripts manually first
  • Use comments to document jobs
  • Keep scripts in a dedicated directory
  • Make scripts executable (chmod +x)

✗ Don't

  • Use relative paths
  • Assume your environment is available
  • Ignore output (redirect to /dev/null blindly)
  • Forget to make scripts executable
  • Schedule intensive jobs during peak hours
  • Use crontab -r casually
  • Leave test entries (* * * * *) running
  • Assume jobs ran - verify!
# Well-documented crontab example:
PATH=/usr/local/bin:/usr/bin:/bin:/home/student/bin
MAILTO=""

# Morning report - weekdays at 8:30 AM
30 8 * * 1-5 /home/student/scripts/morning-report.sh >> ~/logs/report.log 2>&1

# Daily backup - 2 AM
0 2 * * * /home/student/scripts/backup-home.sh

Key Takeaways

1

at: One-time jobs with flexible time specs. at 4pm tomorrow, manage with atq/atrm

2

crontab: Edit with -e, list with -l, remove with -r. Never use -r carelessly!

3

Cron syntax: MIN HOUR DOM MON DOW CMD. Use * for "every", ranges with -, lists with ,, steps with /

4

Environment: Set PATH/MAILTO in crontab. Use absolute paths. Redirect output to logs.

Next: Managing Temporary Files