So I wanted to write a cron job on my Mac. Just run this script every day at midnight. Nothing fancy.
In standard Unix, this is one line in a crontab:
0 0 * * * /some/script
That’s it. Put that in your crontab and
/some/script will run every day at midnight.
Naturally, I typed
man crontab on my Mac to get started. Apple likes to make weird minor modifications to standard Unix commands sometimes. I found:
(Darwin note: Although cron(8) and crontab(5) are officially supported under Darwin, their functionality has been absorbed into launchd(8), which provides a more flexible way of automatically executing commands. See launchctl(1) for more information.)
More flexible, huh? I’m all about more flexible! Sounds good. Another well thought out Apple improvement, sweeping away 30+ years of Unix cruft at a stroke!
Not quite. Turns out that you must submit jobs to
launchctl as XML files. We all know how much I hate XML files, and this is a great example of why. That one line cronjob becomes this fragile ~20 line XML monstrosity:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<string>Updates statistics for awstats.pl every day at 3AM.</string>
But it defines its own syntax!! I can already hear the XML cheerleaders say. Well, sort of. You can read it, kinda, even if you are unfamiliar with the syntax, but you certainly can’t write it without spending half an hour or more reading about and then absorbing the syntax documentation first. For comparison, you can learn the syntax of a crontab entry in around 1 minute. Hell, the documentation of the crontab syntax is shorter than that one simple launchd plist example, nevermind the 13 page manpage explaining the launchd plist syntax.
More verbosity means more things that can be screwed up, as that discussion thread amply demonstrates. Even extremely simple jobs become ordeals. In this case, the problem stems from trying to do too much in one tool, forgetting the Unix philosophy: do one thing, and do it well. Create reusable, chainable tools, rather than one monolithic beast that tries to do everything. Apple bills launchd as a replacement for init, rc, init.d, rc.d, SystemStarter, inetd/xinetd, atd, crond, and watchdogd. Maybe others, too.
Developing a language powerful enough to capture the functionality of all of those different tools results in a massively complex, fragile, general-purpose language. Developing a tool that does exactly 1 of those things results in a tiny, simple, clean, resilient language that is easy to use and hard to screw up. Complex cases are then handled by gluing those many small tools together, not by programming a massive monolith.
Fortunately, cron is still supported by Mac OS X as a legacy service, so I’m using that. If and when they finally remove it from the OS altogether, I’ll be using the MacPorts version.