launchd
may do what you need. As long as your system isn't asleep the entire day, and it is scheduled using the StartCalendarInterval
(see below), launchd
will run your script once a day. Here's what I mean:
- If you schedule your script to run at (for example) 12:00 noon, AND your mac is awake at 12:00 noon, then your script will run at 12:00 noon.
- If you schedule your script to run at (for example) 12:00 noon, BUT your mac is asleep at 12:00 noon, then your script will run as soon as your mac wakes up.
IMHO, launchd
has an advantage because it is part of MacOS. The only 3rd party tool you might want to use with it is LaunchControl
(instead of the native launchctl
). LaunchControl
is a GUI-based app used only to help you create/edit your .plist
, and can help managing & troubleshooting if that becomes necessary. The .plist
file contains the instructions that will be used by launchd
to start your program, and can be very simple. And of course you can avoid 3rd party software completely by simply creating the required .plist
"manually" with a text editor.
As mentioned above, the configuration key you need to use in your .plist
to schedule the time to run your job/script is StartCalendarInterval
. According to man launchd.plist
:
Unlike cron which skips job invocations when the computer is asleep, launchd will start the job the next time the computer wakes up. If multiple intervals transpire before the computer is woken, those events will be coalesced into one event upon wake from sleep.
Example
Here's an example of how to use launchd
to create a User Agent
. The scope of a User Agent
is that it only runs for one user. Note that it is also possible to create Global Agent
, or a Global Daemon
that runs for multiple/all users, but we'll leave that for another day. :
- Create a script in your home directory (
~/
) to output the date and time whenever it's run:
#!/bin/bash
CURRENTDATE=`date +"%c"`
echo Current Date and Time is: ${CURRENTDATE}
Name the script echodatetime.sh
& make it executable:
$ chmod 755 ~/echodatetime.sh
- Create a .plist file in ~/Library/LaunchAgents/sdm.simple.exampleofPLIST.plist:
EDIT: NOTE! DO NOT USE ~/
as shortcut for user's home directory! You must use a full path specification, or it won't 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>Disabled</key>
<false/>
<key>Label</key>
<string>seamus.simple.example</string>
<key>ProgramArguments</key>
<array>
<string>/Users/seamus/echodatetime.sh</string>
</array>
<key>StandardErrorPath</key>
<string>/Users/seamus/echodatetime.error.txt</string>
<key>StandardOutPath</key>
<string>/Users/seamus/echodatetime.log.txt</string>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>25</integer>
</dict>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>26</integer>
</dict>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>27</integer>
</dict>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>28</integer>
</dict>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>29</integer>
</dict>
</array>
</dict>
</plist>
This .plist will cause ~/echodatetime.sh
to be executed at the following times every day:
01:25
01:26
01:27
01:28
01:29
You can change the integer
values in the array for the key StartCalendarInterval
to get the timing that suits you. This is just an example that I used for testing.
The example above shows a "burst" of five (5) runs at one minute intervals. This was done to show the somewhat odd syntax required for such a schedule. Scheduling a single event to run once a day, every day, at 12:00 noon may be accomplished by substituting the simpler StartCalendarInterval
key shown below into the .plist
shown above:
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>12</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
load
your job & check that it's running:
$ launchctl load ~/Library/LaunchAgents/sdm.simple.exampleofPLIST.plist
$ launchctl list | grep seamus
- 0 seamus.simple.example
- Monitor the output file declared in the
StandardOutPath
key:
$ tail -f ~/echodatetime.log.txt
After load
ing the .plist file, you will see the datetime output at the times designated in the StartCalendarInterval
key: at one minute intervals beginning at 01:25 and ending at 01:29 if you use my .plist file.
Miscellany
The .plist
files above have been tested, and operated successfully on my macbook pro running Mojave (ver 10.14.6). Also verified the behavior of launchd
when an event schedule occurs during sleep
: The task ran immediately after the mac "woke up", and logged the time it awakened (i.e. not the scheduled time).
Yes, the .plist syntax is arcane! Consider using LaunchControl
instead of manually hacking these files.
A potentially useful hint: You can check the syntax of your .plist file like so:
$ plutil -lint /Users/seamus/Library/LaunchAgents/sdm.simple.exampleofPLIST.plist
/Users/seamus/Library/LaunchAgents/sdm.simple.exampleofPLIST.plist: OK
- When you're finished with this example, you may
remove
the job from launchd
, and then delete or move the file from your folder ~/Library/LaunchAgents
. Otherwise, your job will be re-started by launchd
next time you log in:
$ launchctl remove seamus.simple.example
$ mv ~/LaunchAgents/sdm.simple.exampleofPLIST.plist ~/archive
Alternatively, leave the file where it is, and set the Disabled
key to true
in the .plist:
<key>Disabled</key>
<true/>
sudo defaults write com.apple.loginwindow LoginHook /path/to/whatever.sh
(as described in this answer)caffeinate
??launchd
. I'll write this up as a proper answer below below. The only caveat I can think of is this: Your script will run once a day, as long as your system is not asleep (or OFF obv.) the entire day. I'll explain below...