Schedule with launchd
Write a minimal LaunchAgent
Create a valid property list with a unique label, explicit program arguments, and one deliberate trigger.
10 minute lesson
A launchd job is described by a property list: an XML file that says what to run and when. Here is a complete, minimal LaunchAgent:
<?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.flaviocopes.screenshot-sorter</string>
<key>ProgramArguments</key>
<array>
<string>/Users/flavio/bin/sort-screenshots</string>
</array>
<key>StartInterval</key>
<integer>900</integer>
</dict>
</plist>
Save it as ~/Library/LaunchAgents/com.flaviocopes.screenshot-sorter.plist. Matching the filename to the label is a convention worth keeping: six months from now, the filename is how you will find the job.
Three keys, three decisions.
Label is the job’s identity in your user’s launchd domain. Use a reverse-domain label, unique on the machine. Every launchctl command you run later refers to it.
ProgramArguments lists the executable and its arguments, one <string> per element. Use an absolute executable path, because launchd resolves nothing for you. And do not put shell operators inside ProgramArguments; launchd starts the executable directly, so a > or && in there arrives as a literal argument to your program, not as redirection. If the job needs shell features, put them inside the script.
StartInterval is the trigger: run every 900 seconds. Other triggers exist — StartCalendarInterval for clock times, WatchPaths for reacting to file changes, RunAtLoad for login — but keep the first job small. Add only one trigger, then prove when and why it runs before adding more conditions.
Validate the complete file with plutil -lint:
plutil -lint ~/Library/LaunchAgents/com.flaviocopes.screenshot-sorter.plist
# .../com.flaviocopes.screenshot-sorter.plist: OK
A malformed plist is the most common first failure, and launchd’s own complaint about it is unhelpfully generic. plutil points at the exact line. Nothing runs yet, though: writing the file registers nothing with launchd. Loading it comes in a later lesson, after we pin down the job’s environment.
Lesson completed