3

On most modern unixes, cron supports individual crontab files in /etc/cron.d, in which each task executes as a specified user. These files automatically update the crontab once edited. They allow individual packages to install their own automated tasks without polluting a global crontab or using a user-specific crontab.

OSX doesn't appear to support this — is that true?

If so, what's the best way of implementing it? Due to the fact that I am developing on OSX, but running production code on Linux, I need to use crond rather than launchd, even if the latter has potential benefits.

migrated from serverfault.com Nov 10 '14 at 18:46

This question came from our site for system and network administrators.

  • This question might get better answers on apple.stackexchange.com – HBruijn Nov 10 '14 at 10:54
  • They are often run by entries in /etc/crontab or anacrontab. – Iain Nov 10 '14 at 11:01
  • @HBruijn Fair point. I posted it here because it's very much server admin specific, and knowledge of both 'traditional' unix systems and osx would probably help in answering the question. – Bobby Jack Nov 10 '14 at 11:02
2

On OS X you should use launchd. To implement it, I will explain with an example.

Go to folder /Users/your-username/Library/LaunchAgents and save there the following plist file. I named it com.username.testscript.plist, but feel free to change it.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.your-username.testscript</string>

  <key>ProgramArguments</key>
  <array>
    <string>/Users/your-username/bin/testscript.sh</string>
  </array>

  <key>Nice</key>
  <integer>1</integer>

  <key>StartInterval</key>
  <integer>60</integer>

  <key>RunAtLoad</key>
  <true/>

  <key>StandardErrorPath</key>
  <string>/tmp/com.your-username.testscript.err</string>

  <key>StandardOutPath</key>
  <string>/tmp/com.your-username.testscript.out</string>
</dict>
</plist>

The file is pretty self-explanatory. It will launch the command /Users/your-username/bin/testscript.sh every 60 seconds, will be launched at load, will save errors on /tmp/com.username.testscript.err and logs on /tmp/com.username.testscript.out.

You can also use directories /Library/LaunchAgents/ or /Library/LaunchDaemos/.

  • I've edited the question to explain why cron really is a must, although your example will probably still come in handy anyway, so thanks very much. – Bobby Jack Nov 10 '14 at 22:48
2

You can use crontabs as usual on OS X, with the minor difference that the per-user files are in /usr/lib/cron/tabs/. The corontab command knows how to access them. Note that the cron daemon doesn't run by default on OS X, but launchd will start it if there are any files in /usr/lib/cron/tabs/, or if /etc/crontab exists.

Speaking of launchd: as @jherran said, launchd items are the more normal way to do this sort of thing on OS X, but they're a bit different from cron jobs:

  • There are two main types of launchd items: LaunchDaemons (which generally run as root), and LaunchAgents (which run as normal users, but only within a login session). LaunchAgents can also be installed either system-wide in /Library/LaunchAgents (where they'll run for any user that logs in) or per-user in ~/Library/LaunchAgents (where they run only for that user). LaunchDaemons can only be installed system-wide.

    By contrast, cron jobs (including per-user jobs) run independently of any login session.

  • cron jobs get skipped if the computer is asleep or off at the scheduled start time. launchd items get run at the next opportunity (i.e. after waking, starting up, logging in, etc), although multiple missed runs will be coalesced into a single run.

  • cron starts its jobs in a fire-and-forget fashion; launchd watches over its children, and if they crash or exit, may restart them (see the KeepAlive key in man launchd.plist) and/or clean up (i.e. kill) any subprocesses they started (see the AbandonProcessGroup key). If your job daemonizes itself, this can cause a lot of trouble if you don't adjust the relevant settings in the .plist file.

  • launchd supports way more options for when an item should be launched: specific times, time intervals, many different kinds of events, just always keep it running... Again, see man launchd.plist.

  • Finally, the format of the command in a launchd.plist file throws a lot of people. launchd doesn't run commands through a shell, so things like variable references, ~ in paths, I/O redirects, etc don't work. Also, it doesn't take a command line, it takes an "array" of words in the ProgramArguments key that're treated essentially as the command and its arguments. e.g. /path/to/somecommand -xv "arg number 1" arg2 arg3 >>/tmp/somecommand.log would be written as:

    <key>ProgramArguments</key>
    <array>
        <string>/path/to/somecommand</string>
        <string>-xv</string>
        <string>arg number 1</string>
        <string>arg2</string>
        <string>arg3</string>
    </array>
    <key>StandardOutPath</key>
    <string>/tmp/somecommand.log</string>
    

    There is also a Program key, but don't use it unless you understand execvp's arguments well. Seriously, just don't.

    If you need to have your command interpreted by a shell, you can invoke one explicitly:

    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>-c</string>
        <string>~/bin/somecommand -xv "arg number 1" arg2 arg3 >>~/Desktop/somecommand.log</string>
    </array>
    
  • Well, you've definitely sold me on launchd more than I was before, and I will hopefully check it out for any future OSX-only tasks. However, as you'll see by my question edit, this one needs to be OSX/unix portable. Regarding your mention of cron, that's just user-specific crontab files, though, right? I'm after that user-independent files that tend to be application-specific, that reside in /etc/cron.d/ – Bobby Jack Nov 10 '14 at 22:50
  • Ah, I misunderstood; in that case, the answer is that OS X's version of cron doesn't have that feature. But actually, I'd argue that since you'd have to do it differently on OS X anyway (adding a line to /etc/crontab rather than dropping a file in /etc/cron.d), you might as well go all the way and drop a file in /Library/LaunchDaemons instead. – Gordon Davisson Nov 10 '14 at 23:22
0

If you look in the directory /usr/lib/cron there is a subdirectory there called jobs which I can only assume is the same as /etc/cron.d.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.