FreeBSD: working with cron April 29, 2010
cron is a very useful tool which permits the scheduling of tasks. cron can run a specified program at a specified time or interval, as
a specified user, capture all the output and either mail it to you, or write it to disk (docs: handbook).
cron does however come with its own set of tricks and traps, which I am documenting below.
cron, crontab files, and crontab
A crontab file specifies all the details of every task to run (docs: manpage).
Crontab files are executed top-to-bottom. Ensure to set all variables before defining tasks.
There is also a program, crontab, which is used to edit users' crontab files (docs: manpage).
The cron daemon monitors every crontab file on the system, and notices all changes automatically (docs: manpage).
system crontab, root crontab, user crontabs
The system crontab's syntax varies slightly from a user's crontab syntax - it specifies which user to run the task as.
vi can be used to edit the system crontab, which is here: /etc/crontab
In general, the system crontab should be left alone.
Each user (including root) has their own crontab file, separate from the system crontab.
User crontab files are stored in /var/cron/tabs - but they are not intended to be edited directly.
Use crontab -l to show the current user's crontab.
Use crontab -e -u Alice to edit Alice's crontab.
Use crontab -e -u root to edit root's crontab.
A user crontab is, when created, blank. Template (based on /etc/crontab):
# user crontab for FreeBSD
#minute hour mday month wday command
# run a task at 23:00 Mon-Fri
0 23 * * 1-5 /usr/home/username/script.sh
# run a task at 23:00 every 3 days
0 23 */3 * * /usr/home/username/script.sh
# run a task at 23:00 every Monday
0 23 * * 1 /usr/home/username/script.sh
# run a task at 07:30 every day
30 07 * * * /usr/home/username/script.sh
# run a task every 15 minutes
*/15 * * * * /usr/home/username/script.sh
# run a task every hour, at 15 minutes past the start of the hour
15 * * * * /usr/home/username/script.sh
crontab file task syntax
Not covered here - covered in detail in the manpage.
From the docs: "Commands are executed by cron when the minute, hour, and month of year fields match the current time, and when at least one of the two day fields (day of month, or day of week) matches the current time"
A cron job uses the HOME and PATH variables in the crontab when executing tasks - it does NOT use the user's HOME and PATH environment variables.
The system crontab sets the PATH variable to /etc:/bin:/sbin:/usr/bin:/usr/sbin - this is very limited, and can cause "not found" errors in scripts that work perfectly on the commandline.
The system crontab sets the HOME variable to /var/log - if running userland tasks, this can cause datafiles to be stored in /var/log
To fix both problems, always use a user's crontab to run scheduled tasks, not the system crontab.
Jobs run from a user's crontab automatically set the HOME variable to the user's home directory.
Use a PATH statement in the user's crontab to set the path as needed.
Defining the full path to all binaries used in scripts is also recommended.
Consider this sample entry in the system crontab file:
# run the daily event at 7:50AM
50 07 * * * root /usr/home/adminuser/script.sh > /usr/home/adminuser/script.txt 2>&1
Any output from the script (on STDOUT) will be saved to /usr/home/adminuser/script.txt which is overwritten if it exists.
Any errors from the script (on STDERR) will also be saved to /usr/home/adminuser/script.txt. This is due to the final
redirection on the above commandline (the "2>&1"), which redirects STDERR to STDOUT (which in turn is redirected to /usr/home/adminuser/script.txt).
If there is no STDOUT redirection in the cron commandline, all output from the job is emailed to the owner of the job (root, in this case).
If there is no STDERR redirection in the cron commandline, all errors from the job are emailed to the owner of the job (root, in this case).
The destination email address can be overridden with the MAILTO variable (defined in the crontab).
Check that the job runs when it should by using an online testing tool, such as this one
First things first, try running the script from the commandline and verify it is working correctly.
Check the email of the user who runs the cron job (by default, cron will email all output of the job to this user).
Check cron's log at /var/log/cron
In your script, try dumping the environment variables to a file and check they are what you are expecting, especially the username, the PATH and the home directory. Eg:
echo environment: `env` > file.txt
If you, by chance, pasted your cron config from the system crontab into a user's crontab, ensure to remove the reference to the username (the user to run the command as).
If this is not removed, cron will try to use the username as the commandline to execute, and the job will fail, with output emailed from cron to root, with the output:
USERNAME: not found
The system crontab should not be used for anything (including jobs for root, use root's crontab for that), so this mistake should never occur.
A note from the docs: Commands are executed by cron when the minute, hour, and month of year fields match the current time, AND
when AT LEAST ONE of the two day fields (day of month, or day of week) matches the current time.