MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Running Mac OS X Panther

Scheduling Tasks in Panther

by James Duncan Davidson

Related Reading

Running Mac OS X Panther
Inside Mac OS X's Core
By James Duncan Davidson

Computers are all about automation of tasks, and Mac OS X gives you several tools to help execute tasks at certain times of day and even on a regular and repeating basis. After all, if you want to copy a file from one place to another, or download a set of web pages at 5:25 a.m. on the dot, why should you have to get out of bed when the computer can do it for you?

To perform many of the tasks it has to, the system uses a set of utilities called cron and periodic to manage several housekeeping tasks. This includes such things as tidying up log files and updating system databases like the one used by the locate command (discussed in Chapter 3). This chapter shows you how to use these tools. First, however, you'll learn how to properly set the time on your computer to make sure the tasks you're wanting to automate get triggered on time every time.

Setting the Time

Without having the time accurately set on your machine, it's pretty hard to schedule tasks. You can set the time and date on your computer yourself using the Date & Time preference panel (/Applications/System Preferences). If you spend any amount of time connected to the Internet (and who doesn't these days), you should bypass all that and have your computer set its time from a network time server. Simply click the "Set Date & Time automatically" checkbox in the Date & Time preference panel, as shown in Figure 8-1, and select an appropriate time server near you.

A network time server is nothing more than a machine that has an accurate clock and that understands the Network Time Protocol (NTP), which is designed to keep large numbers of machines synchronized with an accurate clock-; typically one of the atomic clocks that provides the most accurate time possible.

By default, the Date & Time preference panel allows you to set your time against one of three servers provided by Apple. One of these servers is located in the U.S., another in Asia, and the third in Europe. You can also set your computer to synchronize its time with any NTP server you choose—such as a server on your local network that is set up by the network administrators. You can choose to synchronize against a number of publicly accessible time servers on the Internet. A list of network time servers is available at http://www.eecis.udel.edu/~mills/ntp/servers.html.

Figure 8-1. The Date & Time Preference panel
Figure 8-1. The Date & Time Preference panel

When you set a time server, the system does the following:

Using iCal to Schedule Tasks

iCal is the personal calendaring application that comes with Mac OS X. iCal features multiple calendars that can be published to other computers and synchronized with .Mac. To schedule something in iCal, you create an event, which is an entry on the calendar that is at a specific time with a specific duration. Events can be one-time occurrences, or they can repeat.

Each event can have an alarm that can display a notice on your computer screen, open a file, or even launch an application at a certain time prior to the event so that it can be ready for you. In addition, alarms can go off even if iCal is not running. iCal uses the iCal Helper application (stored in /Applications/iCal.app/Contents/Resources) to keep track of events and fire them off on schedule whether or not iCal itself is running.

You can use iCal's alarms along with AppleScript to execute just about any kind of task you'd like. To do so requires only three simple steps:

  1. Create an AppleScript application that performs the functionality you want and save it somewhere.
  2. Tip: One logical place to store your scripts is in /Library/Scripts. Anything that you store here will show up in the Script menu, if you've enabled that. To enable the Script menu, go to /Applications/AppleScript and double-click the item Install Script Menu. Here you can store almost anything, including AppleScripts, shell scripts, and even application aliases. (If you're a former Mac OS 9 user, now you've got a replacement for your unconfigurable Apple menu.)
  3. Create a one-time or a repeating event in iCal.
  4. Set the alarm properties on that event to open your AppleScript application.

For example, if you wanted to send a listing of all the files in your Home directory through email every week, you could create an AppleScript with the Script Editor (/Applications/AppleScript) that would do the directory scan and email the results, as shown in Example 8-1.

Example 8-1. An AppleScript to list the files in the Home directory and send them through email

set listing to (do shell script "/bin/ls -l $HOME")
tell application "Mail"
    set the newMessage to (make new outgoing message with properties 
        {subject:"Home dir ls output", content:listing})
    tell newMessage
        make new to recipient with properties {address:"you@somewhere.com"}
    end tell
    send newMessage
end tell

Once you have saved this script as an AppleScript application named ListHomeDir, you can set it up to run in response to an event. The key to running an application when an event is scheduled is to have the alarm set to "Open file" and then select the application as the file to open, as shown in Figure 8-2. Once set, as long as you are logged in to the computer at the time the event is scheduled for, the AppleScript application will be executed. For many tasks, this sort of scheduling works out just fine. But, if you want to run a task every hour, or when you aren't logged in to your Mac, you'll need to go to the command line and use the Unix scheduling tools.

Figure 8-2. Setting a repeating event in iCal to execute a task
Figure 8-2. Setting a repeating event in iCal to execute a task

Using periodic

The periodic tool is designed to organize administrative tasks that need to be performed over and over again at regular intervals. The intervals that periodic supports are: daily, weekly, and monthly. Mac OS X itself has a set of tasks that it runs using the periodic system, including:

The tasks that periodic executes are a set of scripts in the /etc/periodic/daily, /etc/periodic/weekly, and /etc/periodic/monthly directories. To have periodic run your own script, simply add it to one of these directories. For example, if you have a batch of sales reports that you'd like to make a daily snapshot of, you could add the script in Example 8-2 to the /etc/periodic/daily directory.

Example 8-2. A sample periodic script

#!/bin/bash

echo Making daily backup of sales reports

DATE=`/bin/date +%Y-%m-%d`
/bin/mkdir -p /SalesBackups/$DATE
/bin/cp -R /Users/Shared/SalesData/* /SalesBackups/$DATE

periodic also gives you a way to control the order in which scripts run. If you look in the /etc/periodic/daily directory you'll notice that scripts that come with the system start with a number. To have your scripts execute in a particular order, simply prefix them with a number and periodic will take care of ordering their execution, as shown in Example 8-3.

Example 8-3. Listing of the /etc/periodic daily directory


$ ls -l /etc/periodic/daily/
total 24
-r-xr-xr-x  1 root  wheel  1389 30 Aug 20:36 100.clean-logs
-r-xr-xr-x  1 root  wheel  3529 30 Aug 20:36 500.daily

The number in the filename controls the execution order of the scripts. The lower the number, the earlier it will be executed compared to other scripts in the directory. For example, to have the sales backup script in Example 8-2 execute after the rest of the daily tasks, you could save it as /etc/periodic/daily/700.salesbackup.

Viewing the output from periodic

Since periodic runs in the background, any output produced by the scripts is hidden from view. To see what happens, periodic saves the output into the /var/log directory. Daily output is saved to /var/log/daily.out, weekly output is saved to /var/log/weekly.out, and monthly output is saved to /var/log/monthly.out.

cron

The primary tool for scheduling tasks on the command line is the venerable cron. This tool is started automatically by SystemStarter at boot time and runs continuously in the background. Every minute, cron wakes up and consults a set of tables to see if there is anything to be executed at that time, and if so, takes care of executing it. These tables, known as crontab files, are located in two places on the filesystem:

/etc/crontab
The crontab file for the system at large. Each entry in this table represents a command that will be run by the root user and the time that it will be run. Anybody can read this file, but only the root user can edit it.
/var/cron/tabs/
This directory contains the user crontab files for each user on the system who is using cron. These files are hidden and are only visible to the root user, so that other users on the system can't look at each other's crontab files.

The System crontab File

Example 8-4 shows the system crontab as it appears in a default installation. While this file is for system tasks, you should always use the crontab file for your user.

Example 8-4. The system crontab file

# /etc/crontab
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
HOME=/var/log
#
#minute hour    mday    month   wday    who     command
#
#*/5    *       *       *       *       root    /usr/libexec/atrun
#
# Run daily/weekly/monthly jobs.
15      3       *       *       *       root    periodic daily
30      4       *       *       6       root    periodic weekly
30      5       1       *       *       root    periodic monthly

The crontab file format is similar to that of many other Unix utilities. Any line beginning with the hash character (#) is a comment. The first three non-commented lines of the file set the environment that cron will run with. The remaining lines of the crontab file consist of five numbers defining the time pattern at which a particular task is to run. The end of the line contains the command to run. In the case of the system crontab, the command also contains the name of the user under which to run the command. As the file itself indicates, each of the five numbers corresponds to a different time interval, arranged in order of finer to larger granularity. Figure 8-3 describes the settings for each of these fields. In addition to numbers, each field can contain an asterisk (*) character, which means match every possibility for that field.

To interpret the lines in the crontab file in Example 8-4, read the fields for each line from left to right. For example, the first field (after the comments) in the system crontab indicates that periodic daily should be run on the fifteenth minute of the third hour on any day of the month on any given month on any day of the week. This means that periodic daily will run at 3:15 a.m. every day. The second line indicates that the periodic weekly command will run on the thirtieth minute of the fourth hour of any Sunday; that is, it runs every Sunday at 4:30 a.m.

For each of the fields, you can also specify a list or range of numbers. For example, if you wanted to run a command every 15 minutes, you could use the following line:

0,15,30,60 * * * * command

Figure 8-3. The crontab file format
Figure 8-3. The crontab file format

Or, if you wanted a command to only run at 5:00 p.m. on weekdays, you could use the following:

0 5 * * 1-5 command

The User crontab

To set up tasks that will get executed, you have to edit your own personal crontab. You can take a look at what you already have in your crontab file by using the crontab command:

$ crontab -l
crontab: no crontab for duncan

This output means nothing has been scheduled yet. By default, a user account won't have a crontab when it is first set up.

Editing a user crontab

There are two ways to edit your crontab. The first involves using whatever editor you've set up on the command line (for example, vi, Emacs, and pico). The second involves using any editor you want (such as TextEdit or BBEdit) and loading a text file as your crontab. To edit your file on the command line, use the following command:

$ crontab -e

For your first crontab entry, let's add a line that will make your computer say "hello" every minute. To do this, add the following line to your crontab file:

* * * * * osascript -e 'say "hello"'
Tip: If you get stuck in an editor that you are unfamiliar with, remember that you can get out of vi by typing :q! and out of Emacs by typing Control-X then Control-C. See Chapter 3, The Terminal and Shell, for more info about command-line editors.

Now, every minute of every day that your machine is on, it will say "hello" to you, which could become annoying indeed. There are a couple things going on here:

  1. The osascript -e 'say "hello"' command is being issued by your system every minute, based on the five preceding asterisks.
  2. The command uses the default system voice set in the Speech preference panel to speak the word "hello" on cue.

But now that you've got a crontab file installed, you can use crontab to list the file:

$ crontab -l
* * * * * osascript -e 'say "hello"'

The other way to create a crontab file is to use an editor like TextEdit or BBEdit. To get the current crontab out in a form that you can open with any editor, save the file on your hard drive, and then execute the crontab command as follows:

$ crontab mycrontabfile
Tip: This also gives a way to quickly reset the crontab file for a user. By passing the /dev/null file into crontab, the user's crontab will be set to an empty file.
Warning: Using the crontab comment to specify a file is also a good way to accidently lose any cron settings that you have in place. Be sure to check your crontab before loading in a new crontab file.

To retrieve your crontab for editing, you can direct the output using the following command:

$ crontab -l > mycrontabfile

Running Virex from cron

If you've installed the McAfee Virex virus scanner from .Mac, you have the vscanx command-line virus scanner installed at /usr/local/vscanx/vscanx . You can take advantage of this and have your disk scanned for viruses from cron instead of from the GUI. This is a bonus that the virus scanner will run no matter who's logged in to the computer or even if nobody is.

To enable this, delete the .VirexLogin item from list your Startup Items in the Accounts preference panel. Then add a line to your crontab . le to execute /usr/local/vscanx/vscanxwhenever you'd like.

Note that you need to give the full path to vscanx since it isn't installed in one of the standard binary directories, such as /usr/bin.

Additional configuration settings

The cron command on Mac OS X has been enhanced compared to those found on some other Unix variants. For example, you can use the following more readable entries in the time field:

Some special strings can be used in crontab files. Table 8-1 has a list of these strings. Except for the last one, all these strings replace the time fields. The last, @AppleNotOnBattery, can be used in front of a command to prevent it from running when your laptop is disconnected from AC power. This ensures that you don't run disk-intensive tasks when you need your battery the most. For example, if you write a script that copies all your files from your ~/Documents folder to some safe storage location that you want to run only when your PowerBook is plugged in, you would use the following crontab entry:

0 * * * * @AppleNotOnBattery ~/bin/copyfiles

Table 8-1. Special strings that can be used in a crontab

StringDescriptionEquivalent To
@ reboot Run when the system reboots 
@ yearly Run on midnight of January 1 0 0 1 1 *
@ monthly Run at midnight on the first of the month 0 0 1 *
@ weekly Run at midnight each Sunday 0 0 * * 0
@ daily Run every day at midnight 0 0 * * *
@ hourly Run every hour at the top of the hour 0 * * * *
@ AppleNotOnBattery Prevents command from running if the system is on battery 

What About at, batch, atq, and atrm?

If you're coming to Mac OS X from another Unix system, you may be familiar with using the at, batch, atq, or atrmcommands for scheduling tasks. These commands exist in Mac OS X, but they have been disabled by Apple due to power management concerns. If you really need these commands, you must enable the atruncommand in /etc/crontab, but do so at your own risk.

Sleep and cron

The cron system won't execute while your system is asleep. This is because the CPU is powered down and there's just enough happening in your machine to keep the contents of memory ready when you want to wake the system up.

Sometimes this isn't a big deal. For example, if you use a crontab line to remind you to stretch every hour, then you won't mind it not running. However, for other tasks that you would like to have run, it can create a bit of a problem. The best piece of advice is to time tasks that need to be run when your system is less likely to be in sleep mode.

Changing periodic's Execution Time

By default, periodic runs daily tasks at 3:15 a.m., weekly tasks at 4:30 a.m., and monthly tasks at 5:30 a.m., because these are the hours when your system should be idle while you are sound asleep. If your system isn't on 24 hours a day, you might consider changing the times that these tasks run to a time when your Mac will be powered on and somewhat idle. For example, if you wanted the daily tasks to run during your lunch hour, the weekly tasks on Monday at 10:00 a.m. while you're in a meeting, and the monthly tasks at 10:30 a.m., you would edit the /etc/crontab file to match Example 8-5. Since the system crontab doesn't belong to a user, you can't use crontab -e. Instead you must edit the file directly.

Example 8-5. The system crontab with the periodic tasks set at a more reasonable time

15      12       *       *       *       root    periodic daily
0       10       *       *       2       root    periodic weekly
30      10       1       *       *       root    periodic monthly

Further Explorations

For more information about the technologies in this chapter, see the following resources:

You may also be interested in consulting the following manpages:

James Duncan Davidson is a freelance author, software developer, and consultant focusing on Mac OS X, Java, XML, and open source technologies. He currently resides in San Francisco, California.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.