Scheduling Jobs Using Cron, and Anacron

Commands
find
df
chkconfig
crontab
mailq

The cron facility
* There is no command named cron, crond is the daemon, crontab is the utility.

crond
Can you figure out from which run levels cron is started?

$ chkconfig --list|grep cron
crond    0:off 1:off 2:on 3:on 4:on 5:on 6:off
anacron  0:off 1:off 2:off 3:off 4:off 5:off 6:off

Or use the grep utility to search for the start-up symbolic links in /etc/rc.d run-level directories

$ find /etc/rc.d -type l | grep cron
/etc/rc.d/rc0.d/K60crond
/etc/rc.d/rc0.d/K05anacron
/etc/rc.d/rc1.d/K60crond
/etc/rc.d/rc1.d/K05anacron
/etc/rc.d/rc2.d/S90crond
/etc/rc.d/rc2.d/S95anacron
/etc/rc.d/rc3.d/S90crond
/etc/rc.d/rc3.d/S95anacron
/etc/rc.d/rc4.d/S90crond
/etc/rc.d/rc4.d/S95anacron
/etc/rc.d/rc5.d/S90crond
/etc/rc.d/rc5.d/S95anacron
/etc/rc.d/rc6.d/K60crond
/etc/rc.d/rc6.d/K05anacron

Is the cron process running?

$ ps -ef|grep cron
root    1037   1  0 May08 ?   00:00:00 crond

Maintaining cron via the crontab command

User crontab files are stored in /var/spool/cron/<username>.
# ls -l /var/spool/cron
total 2
-rw------- 1 root  sysadmin 284 Mar 15 14:53 pattyo
-rw------- 1 root  root     267 Nov 20 13:05 root

 Creating a simple cron entry using the crontab command

$ crontab -e
01 20 * * * df | sort -n +4

On your Linux machine, system crontab files are configured in /etc/crontab

$ cat /etc/crontab
SHELL=/bin/bash

PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly

Anacron

Take a look at the /etc/anacrontab file

$ cat /etc/anacrontab

1    65    cron.daily      run-parts /etc/cron.daily
7    70    cron.weekly     run-parts /etc/cron.weekly
30   75    cron.monthly    run-parts /etc/cron.monthly

Period in days
(how often to run)
Delay: number of minutes
to wait after boot

Job
Command
1
65
cron.daily
run-parts
7
70
cron.weekly
run-parts
30
75
cron.monthly
run-parts

Focusing on the crontab file stored in /var/spool/cron/<username> 

Even though you system cron files take care of file cleanup, we are going to look at a command to automate the cleanup of your /tmp partition as an example.

From the command line you can check how old a particular file is by typing:
$ ls -l /tmp
Review of the find command... then back to cron

But it could be quite tedious looking for particular files this way. The easy way to check for files beneath a specific directory is to use the find command.

The following command will do a long listing of all the files and directories under /tmp:

    $ find /tmp -exec ls -ls {} \;

  • The string {} is find's way of saying "Do it to each file that matches your criteria."
  • The string \; tells find that the -exec option is finished.
  • Note: You must include the backslash (\) before the semi-colon (;) or this command will NOT work.

    To become more familiar with find, create a file in your /tmp directory named junk-file using touch (touch will create an empty file):

    $ touch /tmp/junk-file
    Use the find command to locate any file beginning with the string "junk" in it in /tmp and do a long listing on it:
    $ find /tmp -name 'junk*' -exec ls -l {} \;
    Use find to delete that file:
    $ find /tmp -name 'junk*' -exec rm -f {} \;
     
  • start searching in /tmp for any file that begins with "junk"
  • if match found, {} will be replaced with that file name in the exec section
  • then the command "rm -f <match>"  will be executed on the matched file
  • Now look for any file with the string "junk" in it
    Note the quotes around the string 

    $ find /tmp -name '*junk*' -exec ls -l {} \;
    Now, getting down to the task at hand... back to cron

    What you really want to know is which files haven't be accessed recently. This find command will list files which haven't been accessed in 7 days:

    $ find /tmp -atime +7 -exec ls -l {} \;
    Note: If you just rebooted, you won't find any files that fit this criteria because /tmp will most likely be empty.

    Let's say you want to delete from /tmp any file or directory that hasn't been access in the past 7 days on a regular basis. This is the type of repetitive task that is perfect for cron.

    The format used by cron contains size fields. Here is an excerpt from the crontab man 5 page describing these 6 fields (man 5 crontab):

          field          allowed values
         -----          --------------
         minute         0-59
         hour           0-23
         day of month   1-31
         month          1-12 (or names, see below)
         day of week    0-7 (0 or 7 is Sun, or use names)

      A field may be an asterisk (*), which matches all

    In the following two examples, the Bourne shell script runme will execute:

    Not very useful. However, it demonstrates the flexibility of the cron facility:
    0 1 10-12 apr sat,sun sh runme
    0 1 10-12 4 6,7 sh runme
    Note: Names can be used for the "month" and "day of the week" fields in Linux.

    Now to create the /tmp cleanup script, first open the crontab editor:

    # crontab -e
    Most likely the editor will default to vi. You can change this by modifying the environment variable.
    For bash
    export EDITOR=vi

    For tcsh or csh
    setenv EDITOR vi

    Type in the following:

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

    Since the asterisks (*) matches everything, when  will this command execute?

    To list the contents of your crontab file:

    $ crontab -l
    Where does the output (and errors) generated by cron go?

    Redirecting output

    0 5 * * * mailq -v | mail -s "Checking mail queue for stuck mail" postmaster

    postmaster: your-account-name
    root: your-account-name
    $ mailq -v | mail -s "Checking mail queue for stuck mail" postmaster

    Redirecting output...

    0 5 * * * find /tmp -atime +7 -exec ls -l {} \; >>/var/log/tmp.log 2>&1

    If you want to discard both standard error and standard out you would write the command like this:

    0 5 * * * find /tmp -atime +7 -exec ls -l {} \; >/dev/null 2>&1

    A short bit on file descriptors

    Almost all I/O operations in Unix are done through file descriptors which are passed the basic read / write system calls.

    Some redirction examples

    To redirect the standard output to mail and the standard error to the log file:

    0 5 * * * (find /tmp -atime +7 -exec ls -l {} \; \
       | mail -s "Older Files" pattyo) >> /tmp/errs.log 2>&1

    To redirect standard error and standard out to mail:

    0 5 * * * find /tmp -atime +7 -exec ls -l {} \; \
       2>&1 | mail -s "Older Files" pattyo

    Finally, the best or most all-inclusive yet!

    To send an email if there is an error  containing contents of log file which includes both standard error and standard out :

    0 5 * * * (find /tmp -atime +7 -exec ls -l {} \; \
       || mail -s "Older Files Output" pattyo \
       < /tmp/errs.log) >> /tmp/errs.log 2>&1

    Why does the second command redirect both stdout and stderr to a file but the first one only redirects stdout?

    $ cat food 2>&1 > file
    $ cat food > file 2>&1
    Example 1:

    Example 2:

    Passing output through the pipe behaves differently:

    $ cat food  | mail pattyo 2>&1
    $ cat food  2>&1 | mail pattyo

    If you can follow this you're amzing! Basically, the order of the redirectors matters.

    Redirection with tee

    There is one other interesting thing you can do with redirection.

    $ who | sort | tee who.sort | lpr
    The tee command saves a copy of the sorted list of users in the file who.sort and also pipes it to the next command in the chain, lpr.

    Back to CRON

    0 5 * * * (date; find /tmp -atime +7 -print -exec /bin/rm -f {} \; || mail -s "Older Files Removed" pattyo < /var/log/tmp_cleanup.log) >> /var/log/tmp_cleanup.log 2>&1