Cron Tutorial

For Time-Based Job-Scheduling

Cron Table Example

Introduction

   Have you ever wanted to check for updates for that new database you wanted to keep up-to-date? Or maybe just a simple bash script you wanted to run every hour to make sure your computer is alive and well? Here, I present a succinct yet informative tutorial on how to set these up using cron in Unix-like environments.

Features

  • Schedule programs to be run either hourly, daily, weekly, monthly, or even yearly.
  • Setup more interesting scripts that ensure the computer is not being overloaded or no processes going zombie.
  • Create scripts that email you and your team if something has gone awry.

Demonstration

   A daemon is a background process which becomes active upon request. Because of this, daemons can take up very little processing power while being always ready. Now wouldn't it be useful if there existed a daemon which could execute a program, go to sleep for a specified amount of time, then repeat ad infinitum? Cron is a job scheduler written originally part of the Unix package, but now it has become standard in Linux distributions as well. Even Mac OS X utilizes cron, but launchd is preferred (and uses XML formatting). So installation on these operating systems shouldn't be necessary. But if you are a Windows/Cygwin user, check out: Windows Task Scheduler(Win GUI), cronw(Win), or Vixie cron(Cygwin). This tutorial will be using the terminal so be prepared. Let's get started.

   Open up your terminal and type in crontab -e. If the command is not recognized, ensure cron is installed and that it is running (ps -e | grep cron should show something). At this point, if you've never set this up before, there will be a prompt to choose which text editor to use. If you're a pro and a masochist, use vim, else use the layman's nano which should be recommended. Now you'll see the glorious commented out help info.

  1. crontab -e
  2. no crontab for user - using an empty one
  3. Select an editor. To change later, run 'select-editor'.
  4.   1.  /bin/ed
  5.   2.  /bin/nano      <---- easiest
  6.   3.  /usr/bin/vim.basic
  7.   4.  /usr/bin/vim.tiny
  8. Choose 1-4 [2]: 2
More

   Let's break it down. At the bottom of the help info is: "m h dom mon dow command". The instructions say what these are but here's for memory's sake.

  • m - minute: What minute the command will be run on [0-59].
  • h - hour: What hour the command will be run on [0-23] (military time - so 0 is midnight).
  • dom - day of month: What day of the month the command will be run on [1-31] (some months have different amounts of days so be weary).
  • mon - month: What month the command will be run on [1-12 OR JAN-DEC].
  • dow - day of week: What day of the week the command will be run on [0-6 OR SUN-SAT] (0 = Sunday, 1 = Monday, etc.).

   If you leave a star in any of these categories, cron will ignore the specification. Here's some examples for your cognitive pleasure.

Expression Command
* 10 * * * echo "This will be run every day at 10AM."
30 10 * * * echo "This will be run every day at 10:30AM."
30 * * * * echo "This will be run every hour at the middle of an hour."
* * 1 JAN * echo "This will be run every year at January 1st at midnight."
* 21 * * 5 echo "This will be run every Friday at 9PM."

   All cron's are equally good, but some are more equally good than others. Later versions of cron allow for much more flexibility in timing. Before if you wanted something ran every 15 minutes, you'd have to make 4 entries into the cron tab with only the 'm' flag changed to 0, 15, 30, and 45. In Vixie cron (which is so popular you may be using it as we speak), features such as lists and ranges were added. Some allow for other non standard characters (such as the Quartz java scheduler) for indicating times like the last Friday of a month. And some add more time specifications like adding columns for seconds and years. For the sake of completeness, here's more examples of these cool features, followed by everyone's favourite: more examples.

  • , - comma: used to separate items within a list.
  • - - hyphen: used to indicate a range between two end points.
  • % - percent: when used in the command and if not backslashed, will act as a newline character.
  • # - octomorph: used in "dow" which is then followed by a number between 1 to 5; indicates which nth weekday/weekend of the month (ex FRI#2 = 2nd Friday of the month).
  • ? - question mark: either analogous to '*' for "dom" and "dow" or in other implementations gets substituted for the times the cron daemon was started.
  • / - forward slash: used for step values
  • L - last: when used in "dow", used for last weekday/weekend of the month (ex last Friday); in "dom", last day of the month
  • W - weekday: used in "dom" to find the closest weekday (can go backwards or forwards to find nearest, but will stay within the month)
Expression Command
* 10 * * MON,WED,FRI echo "This will be run every Monday, Wednesday, and Friday at 10PM."
* 10 * * MON-THU mail -s "Don't be late" me%This will be run Monday through at 10PM.
* 9-12,14-17 * * 6#3 echo "This will be run every hour between 9AM-12PM and 2-5PM on the 3rd Friday."
*/15 * L * * echo "This will be run every 15 minutes on the last day of the month."
*/20 14,23 4W,L 5-7 ?,2#1 echo "This will run every 20 minutes at 2PM and 11PM on the closest weekday from the 4th, the last day of the month, the days of the week that the cron daemon was started, and the first Monday for the months of May to July, you psycho."

   So now we're ready to add some commands to be run. Say for example you were interested in ensuring that your server wasn't overloaded, and you wish to be informed if this happened. This could indicate some attacker spamming requests which your lowly server cannot handle. Here's some code:

  1. #!/bin/bash
  2. #Used to see if load is above maximum threshold
  3. MAXLOAD=$(cat /proc/cpuinfo | grep -c processor)
  4. current=$(cat /proc/loadavg | awk '{print $1}')
  5. bool=$(echo $current'>'$MAXLOAD | bc -l)
  6. if [ $bool -gt 1 ]; then
  7.   emails=($(cat /path/to/emails/emails.txt))
  8.   output="path/to/emails/emailLoad.txt"
  9.   echo -e "Load is greater than maximum!\n\n" > $output
  10.   echo -e "Current: $current\nMaximum: $MAXLOAD\n\n" >> $output
  11.   echo "Please elucidate the problem at hand" >> $output
  12.   for email in ${emails[@]}; do
  13.     cat $output | mail -s "Server Load Over Maximum" $email
  14.   done
  15. fi
More

   I personally used to make a temporary file to dump all the text just to have a local copy for reference (and to see if it works when something goes awry). You can remove it automatically if you'd like. Also if you didn't realize, when I write "/path/to/???/", you should put the location of where these files exist yourself. Let's add this to the cron table.

  1. crontab -e
  2. # Edit this file to introduce tasks to be run by cron.
  3. ...
  4. # m h  dom mon dow   command
  5. */15 * * * * bash /path/to/program/LoadCheck.sh
More

   If you're using nano, press CTRL+O then hit ENTER to write and CTRL+X to exit. And if you're using vim, you did this to yourself. Now we're done! Cron was made with the user in mind. As you can see what we wrote should execute every 15 minutes; of course you're free to change this to fit your personal whims. If you're curious to what other scripts I have my server unconsciously run, I wrote ZombiePrograms.sh for checking 'zombie' jobs. They're not explicitly zombies (i.e. they don't show up as zombie when you run top), but seem to be stuck in a huge loop. And for bioinformatics people, there's UpdateBlastDB.sh which you can use to run every day or every week.