Cron Recipes

cron is one of those tools better shown than explained. This quick guide does just that. We'll go over Vixie's cron, the flavor present in most Unixes nowadays. I only cover stuff that I find most useful. At the end of the document I list some gotchas that may help you resolve some issues. To dig deeper, I suggest to consult the manual (man 5 crontab from the command line). For this tutorial, I'll be working from a bash shell.


Basic usage

cron jobs are set in a crontab file.

Editing your crontab file from the shell:

$ crontab -e

Listing your cron jobs

$ crontab -l

Removing your crontab file

$ crontab -r

crontab file usage

*  *  *  *  *  command
-  -  -  -  -
|  |  |  |  |
|  |  |  |  +----- day of week (0 - 6) (Sunday=0)
|  |  |  +-------- month (1 - 12)
|  |  +----------- day of month (1 - 31)
|  +-------------- hour (0 - 23)
+----------------- min (0 - 59)

- a single value: 1 
- a range of values: 1-5
- all values: * 
- a list of values: 3,5,8 
- a repeat patterns: 2/3 (see examples for explanation)
- if both dom and dow are specified they will be executed on their respective schedule 
- months and days of the week can be specified by name 
- some special convenient strings you can use as shortcuts

String      ||    Equivalent
----------------------------------------
@yearly     ||    0 0 1 1 *
@annually   ||    0 0 1 1 *
@monthly    ||    0 0 1 * *
@weekly     ||    0 0 * * 0
@daily      ||    0 0 * * *
@midnight   ||    0 0 * * *
@hourly     ||    0 * * * *
@reboot     ||    Run once, at startup.
----------------------------------------

Examples of cron schedules

# each min
* * * * *

# each hour
0 * * * *

# each hour, after 5 min
5 * * * *

# every day at 4:35pm
35 16 * * *

# the 1st of each month at midnight
0 0 1 * *

# the 1st and 15th of each month at noon
0 12 1,15 * *

# every hour, on the 1st of april
0 * 1 4 *

# the 25th of december at midnight (i.e going from 24th to 25th)
0 0 25 12 *

# every fridays at 5pm
0 17 * * 5

# every thursdays at 5pm, only in august
0 17 *

# aug thur at 9am, noon and 5pm, from monday to friday
0 9,12,17 * * 1-5

# thursday every two weeks at 6am (i.e. every 2 times it hits the 4th day of the week)
0 6 * * 4/2

# every 5 minutes (i.e. every 5 times it hits a minute, from the start of the job)
*/5 * * * *

# at 6am every 10 days (i.e. every 10 times it reaches 6am)
0 6/10 * * *

# at reboot 
@reboot /home/mike/my_ini_script.sh
Last Day Of The Month

cron doesn't have any native facility to get the LDOTM. With a bit of logic though, and some basic shell tools, we can achieve this. Today being the last day of the month implies a number of truisms one can use to implement an algorithm:

  • tomorrow will be the 1st day of some month
  • tomorrow's month will be different from today's
  • tomorrow's day of the month will be less than today's

Let's implement a solution based on the first one, if today is the last day of the month, then tomorrow will be the first day of some month. We'll use the date tool, which is available on most GNU systems. If your version of date isn't able to do these operations, look for some alternatives scripts to help you with the task. Popular options are scripts combining cal, sed and awk. But I think most people should be fine with this:

Cron job running on the LDOTM:

0 0 28-31 * * [  `date --date=tomorrow +\%d` -eq '01'  ] && our_command

Explanation:

  • First, I set the job to attempt the command only from the 28th to the 31st each month.
0 0 28-31 * *

The job could in fact be scheduled to run once everyday. Limiting it to the last 4 days of the month is a practically useless optimization, purely for aesthetical purpose. With today's processing power, it would be equivalent to removing a handfull of toothpicks from a cargo ship.

  • getting the date from the command line
`date`
^____^
   |
  these are backticks, not single quotes
  • getting tomorrow's date:
`date --date=tomorrow`
  • getting just the day number:
`date --date=tomorrow +%d`
  • let's escape the percent symbol for use within crontab (see gotchas):
    `date --date=tomorrow +\%d`
  • let's compare that number to '01', the first day of any month:
[ `date --date=tomorrow +\%d` -eq '01' ]
 ^------------------------------------^
                   |
        spaces here are required
  • if the previous expression evaluates to true then boolean logic allows us to try to evaluate the next expression, which is where we put the command.
[ `date --date=tomorrow +\%d` -eq '01' ] &&  our_command
  ------------------------------------   --  -----------
               |                          |       |
             if true                     then   command

cron permissions

2 lists allow to control permissions, by simply entering one username per line, /etc/cron.allow and /etc/cron.deny. cron permissions for all users depend on the allow/deny lists existence.

             | allow exists |  allow not exists  |
-------------+--------------+--------------------|
deny exists  | allow only   |   all except deny⚐ |
-------------+----------+------------------------|
deny !exists | allow only   | system dependant⚑  |
-------------+--------------+--------------------+

⚐ you're allowed if you're not on the deny list.
⚑ on some systems all users are allowed, on others only root is allowed.

Generating a log file

To append the command's output to a log file:

15 5 * * * some_command >> /home/someuser/logs/cron/some_command.log

To overwrite or create a new log file:

15 5 * * * some_command > /home/someuser/logs/cron/some_command.log

To log errors:

15 5 * * * some_command 2> /home/someuser/logs/cron/errors.log

Gotchas

  • if the crontab -e command is available, you should not attempt to edit cronjobs otherwise.

  • when you edit crontab with crontab -e, it opens with whichever editor is currently set in the shell's $EDITOR variable. To change this temporarily (i.e. for the current shell window), from the command line type

$ export EDITOR=nano

No space around the = sign. crontab -e will now open with the nano editor. echo $EDITOR tells you which editor is currently set. To make the change permanent, put the export command in one of your shell init scripts (.profile, .bashrc, .zhrc, etc).

  • percent symbols (%) indicate new lines in cron job entries. To use them in the context of a command, escape them with a backslash (e.g. date +\%m).

  • prior to running jobs, cron sets a minimal environment that may or may not include /usr/bin in its $PATH variable. It's therefore generally a good idea to specify the full path to a command in crontab. To find out what that path is, from the shell type

$ which some_command 

where you'll replace some_command with the actual name of the command.

  • to run jobs listed in your crontab file, cron sets its $HOME variable to your $HOME variable (i.e. You, the owner of the crontab file).

  • if you wish for your profile initialization script to be executed prior to a job, you must explicitly specify it in that job's entry. e.g.

0 6 * * * $HOME/.profile && daily_backup.sh
  • the location of cron.allow and cron.deny may vary across Unixes. In Linux they're under /etc, whereas in Solaris for example they're under /etc/cron.d.