launchd plist format for running a command at a specific time on a weekday

I'm trying to run a command at 10 minutes after 2am every weekday only. In my launchd .plist file, how can I specify this?

I don't want it to run every 2 hours and 10 minutes, just once every weekday at the specified time.


Solution 1:

There are a few caveats with creating launchd .plist files. I'll summarize them below:

  1. Each Weekday must be a entry specified in an placed in the StartCalendarInterval dictionary entry.
  2. Weekdays go from 1 to 5. Sunday is 0 and 7 (I know, right?)
  3. You have to know how you want your item to run and place the .plist in the appropriate directories/folders:
    1. ~/Library/LaunchAgents - Specific to the user. Will run when the user is logged in.
    2. /Library/LaunchAgents - Specific to all users. Will run when the user(s) is(are) logged in.
    3. /Library/LaunchDaemons - Runs regardless if user(s) is(are) logged in. Must use sudo 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:

enter image description here

Solution 2:

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.

Solution 3:

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>