Apple - launchd plist format for running a command at a specific time on a weekday
There are a few caveats with creating launchd .plist files. I'll summarize them below:
- Each Weekday must be a entry specified in an placed in the StartCalendarInterval dictionary entry.
- Weekdays go from 1 to 5. Sunday is 0 and 7 (I know, right?)
- You have to know how you want your item to run and place the
.plist
in the appropriate directories/folders:~/Library/LaunchAgents
- Specific to the user. Will run when the user is logged in./Library/LaunchAgents
- Specific to all users. Will run when the user(s) is(are) logged in./Library/LaunchDaemons
- Runs regardless if user(s) is(are) logged in. Must usesudo
to load.plist
My preference is #3 (load as a LaunchDaemon) so my script will get executed whether I am logged in or not. When using as a LaunchDameon, you must use sudo
to load the .plist
:
sudo lauchctl load com.user.fileCleanup.plist
Note: As for the naming convention in my example, I like to use com.user.< name of my script >.plist
This helps me in identifying and debugging any issues should they arise and keeps with the Apple naming convention of com.apple.some-app.plist
This is entirely optional on your part.
Below is a script that I am using to clean up work files (I modified it for your specific time parameters). You can edit this file with your favorite text editor (I use Komodo Edit), but TextEdit or even nano
or vi
from the command line will work.
<?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.user.fileCleanup</string>
<key>ProgramArguments</key>
<array>
<string>/Users/allan/Documents/Scripts/Unix/fileCleanup.sh</string>
</array>
<key>StartCalendarInterval</key>
<!-- Weekdays are 1 - 5; Sunday is 0 and 7 -->
<array>
<dict>
<key>Weekday</key>
<integer>1</integer>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>2</integer>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>3</integer>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>4</integer>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>5</integer>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
</array>
</dict>
</plist>
Bonus Footage....
This next part is optional...
So that I can get some feedback that my scripts executed and what the result was, I created a small AppleScript function that invokes the display notification
(it's quite simple for my needs, but obviously could be improved).
on run argv
set Message to item 1 of argv
set Title to item 2 of argv
set aud to item 3 of argv
set STitle to "Terminal"
set Snd to "Blow.aiff"
if (aud = "sound") then
display notification Message with title Title subtitle STitle sound name Snd
else
display notification Message with title Title subtitle STitle
end if
end run
Within my script, I check for success or failure, depending on what that is (i.e. a file existing in a particular directory), it will generate a notification. If not, another notification, but this time with sound!
#!/bin/bash
if [ -f "$SOURCE" ]
then
scp -q "$SOURCE" "$TARGET"
osascript $ASCRIPT_DIR/notify.scpt "Podcast Transfered successfully" "$TITLE -- SUCCESS" "nosound"
else
osascript $ASCRIPT_DIR/notify.scpt "The file $TARGET does not exist" "$TITLE -- ERROR" "sound"
fi
Here's the sample output:
Here is an example .plist file to cover executing at 2:10 AM every weekday.
<?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.me.hello</string>
<key>ProgramArguments</key>
<array>
<string>say</string>
<string>hello</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>1</integer>
</dict>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>2</integer>
</dict>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>3</integer>
</dict>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>4</integer>
</dict>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>5</integer>
</dict>
</array>
</dict>
</plist>
If you do not want to hand write the launchd
.plist file, a program like Lingon X can be quite helpful as it will facilitate the loading/unloading of the .plist file too.
The command you need is Start CalendarInterval
The easy way is use a program like LaunchControl or Lingon to write and load the plist.
This example runs /usr/bin/env at the time you want. There is no shortcut for all weekdays so you have to list the days Sunday is 0. I have shown Monday and Tuesday as an example
<?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>local.job</string>
<key>Program</key>
<string>/usr/bin/env</string>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>1</integer>
</dict>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>10</integer>
<key>Weekday</key>
<integer>2</integer>
</dict>
</array>
</dict>
</plist>