Book HomeRunning LinuxSearch this book

8.2. Scheduling Jobs Using cron

The original purpose of the computer was to automate routine tasks. If you must back up your disk at 1:00 A.M. every day, why should you have to enter the commands manually each time--particularly if it means getting out of bed? You should be able to tell the computer to do it and then forget about it. On Unix systems, cron exists to perform this automating function. Briefly, you use cron by running the crontab command and entering lines in a special format recognized by cron. Each line specifies a command to run and when to run it.

Behind your back, crontab saves your commands in a file bearing your username in the /var/spool/cron/crontabs directory. (For instance, the crontab file for user mdw would be called /var/spool/cron/crontabs/mdw.) A daemon called crond reads this file regularly and executes the commands at the proper times. One of the rc files on your system starts up crond when the system boots. There actually is no command named cron, only the crontab utility and the crond daemon.

On some systems, use of cron is limited to the root user. In any case, let's look at a useful command you might want to run as root and show how you'd specify it as a crontab entry. Suppose that every day you'd like to clean old files out of the /tmp directory, which is supposed to serve as temporary storage for files created by lots of utilities.

Most systems remove the contents of /tmp when the system reboots, but if you keep it up for a long time, you may find it useful to use cron to check for old files (say, files that haven't been accessed in the past three days). The command you want to enter is:

ls -l filename

But how do you know which filename to specify? You have to place the command inside a find command, which lists all files beneath a directory and performs the operation you specify on each one.

We've already seen the find command in the section "Section 8.1.2, "Incremental Backups"." Here, we'll specify /tmp as the directory to search, and use the -atime option to find files whose last access time is more than three days in the past. The -exec option means "execute the following command on every file we find":

find /tmp \! -type d -atime +3 -exec ls -l {} \;

The command we are asking find to execute is ls -l, which simply shows details about the files. (Many people use a similar crontab entry to remove files, but this is hard to do without leaving a security hole.) The funny string {} is just a way of saying "Do it to each file you find, according to the previous selection material." The string \; tells find that the -exec option is finished.

Now we have a command that looks for old files on /tmp. We still have to say how often it runs. The format used by crontab consists of six fields:

minute   hour   day   month   dayofweek   command

Fill the fields as follows:

  1. Minute (specify from 0 to 59)

  2. Hour (specify from 0 to 23)

  3. Day of the month (specify from 1 to 31)

  4. Month (specify from 1 to 12, or a name such as jan, feb, and so on)

  5. Day of the week (specify from 0 to 6 where 0 is Sunday, or a name such as mon, tue, and so on)

  6. Command (can be multiple words)

Figure 8-1 shows a cron entry with all the fields filled in. The command is a shell script, run with the Bourne shell sh. But the entry is not too realistic: the script runs only when all the conditions in the first five fields are true. That is, it has to run on a Sunday that falls on the 15th day of either January or July--not a common occurrence! So this is not a particularly useful example.

Figure 8-1

Figure 8-1. Sample cron entry

If you want a command to run every day at 1:00 A.M., specify the minute as 0 and the hour as 1. The other three fields should be asterisks, which mean "every day and month at the given time." The complete line in crontab is:

0 1 * * * find /tmp -atime 3 -exec ls -l {} \;

Since there are a lot of fancy things you can do with the time fields, let's play with this command a bit more. Suppose you want to run the command just on the first day of each month. You would keep the first two fields, but add a 1 in the third field:

0 1 1 * * find /tmp -atime 3 -exec ls -l {} \;

To do it once a week on Monday, restore the third field to an asterisk but specify either 1 or mon as the fifth field:

0 1 * * mon find /tmp -atime 3 -exec ls -l {} \;

To get even more sophisticated, there are ways to specify multiple times in each field. Here, a comma means "run on the 1st and 15th day" of each month:

0 1 1,15 * * find /tmp -atime 3 -exec ls -l {} \;

while a hyphen means "run every day from the 1st through the 15th, inclusive":

0 1 1-15 * * find /tmp -atime 3 -exec ls -l {} \;

and a slash followed by a 5 means "run every fifth day" which comes out to the 1st, 6th, 11th, and so on:

0 1 */5 * * find /tmp -atime 3 -exec ls -l {} \;

Now we're ready to actually put the entry in our crontab file. Become root (since this is the kind of thing root should do) and enter the crontab command with the -e option for "edit":

rutabaga# crontab -e

By default, this command starts a vi edit session. If you'd like to use Emacs instead, you can specify this before you start crontab. For a Bourne-compliant shell, enter the command:

rutabaga# export VISUAL=emacs
For the C shell:
rutabaga# setenv VISUAL emacs

The environment variable EDITOR also works in place of VISUAL for some versions of crontab. Enter a line or two beginning with hash marks (#) to serve as comments explaining what you're doing, then put in your crontab entry:

# List files on /tmp that are 3 or more days old.  Runs at 1:00 AM
# each morning.
0 1 * * * find /tmp -atime 3 -exec ls -l {} \;

When you exit vi, the commands are saved. Look at your crontab entry by entering:

rutabaga# crontab -l

We have not yet talked about a critical aspect of our crontab entry: where does the output go? By default, cron saves up the standard output and standard error and sends them to the user as a mail message. In this example, the mail goes to root, but that should automatically be directed to you as the system administrator. Make sure the following line appears in /usr/lib/aliases (/etc/aliases on Debian):

root: your-account-name

In a moment, we'll show what to do if you want output saved in a file instead of being mailed to you.

Here's another example of a common type of command used in crontab files. It performs a tape backup of a directory. We assume that someone has put a tape in the drive before the command runs. First, an mt command makes sure the tape in the /dev/rft0 device is rewound to the beginning. Then a tar command transfers all the files from the directory /src to the tape. A semicolon is used to separate the commands; that is standard shell syntax:

# back up the /src directory once every two months.
0 2 1 */2 * mt -f /dev/rft0 rewind; tar cf /dev/rft0 /src

The first two fields ensure that the command runs at 2:00 A.M., and the third field specifies the first day of the month. The fourth field specifies every two months. We could achieve the same effect, in a possibly more readable manner, by entering:

0 2 1 jan,mar,may,jul,sep,nov * mt -f /dev/rft0 rewind; \
          tar cf /dev/rft0 /src

The section "Section 8.1, "Making Backups"" explains how to do backups on a regular basis.

The following example uses mailq every two days to test whether there is any mail stuck in the mail queue, and sends the mail administrator the results by mail. If there is mail stuck in the mail queue, the report includes details about addressing and delivery problems, but otherwise the message is empty:

0 6 */2 * * mailq -v | \
          mail -s "Tested Mail Queue for Stuck Email" postmaster

Probably you don't want to receive a mail message every day when everything is going normally. In the examples we've used so far, the commands do not produce any output unless they encounter errors. But you may want to get into the habit of redirecting the standard output to /dev/null, or sending it to a log file like this (note the use of two > signs so that we don't wipe out previous output):

0 1 * * * find /tmp -atime 3 -exec ls -l {} \; >> /home/mdw/log

In this entry, we redirect the standard output, but allow the standard error to be sent as a mail message. This can be a nice feature, because we'll get a mail message if anything goes wrong. If you want to make sure you don't receive mail under any circumstances, redirect both the standard output and the standard error to a file:

0 1 * * * find /tmp -atime 3 -exec ls -l {} \; >> /home/mdw/log 2>&1

When you save output in a log file, you get the problem of a file that grows continuously. You may want another cron entry that runs once a week or so, just to remove the file.

Only Bourne shell commands can be used in crontab entries. That means you can't use any of the convenient extensions recognized by bash and other modern shells, such as aliases or the use of ~ to mean "my home directory." You can use $HOME, however; cron recognizes the $USER, $HOME, and $SHELL environment variables. Each command runs with your home directory as its current directory.

Some people like to specify absolute path names for commands, like /usr/bin/find and /bin/rm, in crontab entries. This ensures that the right command is always found, instead of relying on the path being set correctly.

If a command gets too long and complicated to put on a single line, write a shell script and invoke it from cron. Make sure the script is executable (use chmod +x) or execute it by using a shell like:

0 1 * * * sh runcron

As a system administrator, you often have to create crontab files for dummy users, such as news or UUCP. Running all utilities as root would be overkill and possibly dangerous, so these special users exist instead.

The choice of a user also affects file ownership: a crontab file for news should run files owned by news, and so on. In general, make sure utilities are owned by the user in whose name you create the crontab file.

As root, you can edit other users' crontab files by using:

tigger # crontab -u user -e

  Also, think about who is going to use the output files you create. If a file is created by a cron entry running as news, you may have trouble reading the file later as another user. You may have to use chown or chmod in your cron script to make sure the file is usable later. These commands are discussed in the section "Section 4.14, "Changing the Owner, Group, and Permissions"" in Chapter 4, "Basic Unix Commands and Concepts".

Since you can't log in as news, you can edit news's crontab file as root using the command:

rutabaga# crontab -e -u news


Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.