2

I'm looking for a method to run a script (well, a single shell command) when my laptop wakes from sleep.

This question has been asked before, but 8 years ago (for OS X Lion), and several of the answers require third-party products that are now no longer developed.

Another option might be for the script to run at any time, once per day, when it's up and running, but I don't want it to miss an allotted time slot just cause it's not awake. I'm not so bothered about triggering a script at sleep time.

8
  • 2
    The LaunchDaemon method should still work probably. Anyway, in case you already use Keyboard Maestro it allows running a script on wake. – nohillside Nov 27 '19 at 10:48
  • 1
    In addition to LaunchDaemon this method also still works (in Mojave at least) : sudo defaults write com.apple.loginwindow LoginHook /path/to/whatever.sh (as described in this answer) – lx07 Nov 27 '19 at 18:18
  • Q: Does it need to run within a certain interval regardless of sleep state, OR are you happy to have it run whenever it wakes up? That's not clear from your question... – Seamus Nov 28 '19 at 0:42
  • There are some tasks that run when the computer is asleep using the "Power Nap" option (on MacBook Pro, iMac may differ) in the Power Saver tab. Perhaps there's a switch to set your script to run during "Power Nap"? Otherwise, consider using caffeinate?? – Seamus Nov 28 '19 at 0:49
  • 1
    If running once a day is useful, my answer would be to use 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... – Seamus Nov 28 '19 at 14:16
5

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 launchdto 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. :

  1. 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  
  1. 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>  
  1. load your job & check that it's running:
$ launchctl load ~/Library/LaunchAgents/sdm.simple.exampleofPLIST.plist  
$ launchctl list | grep seamus
  -   0   seamus.simple.example 
  1. Monitor the output file declared in the StandardOutPath key:
$ tail -f ~/echodatetime.log.txt  

After loading 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/>  
5
  • 1
    Shouldn't the value in <string>...</string> that have Users/seamus/... have a / in front of Users/seamus/.... e.g. /Users/seamus/...? – user3439894 Nov 29 '19 at 19:05
  • @user3439894: Yes, it should have. Thanks for calling that out. – Seamus Nov 29 '19 at 19:15
  • If this is scheduled to run once per day, then what happens if you sleep the computer twice that day? If it were to run every minute, that isn’t what the OP wants either. I think I’m missing something (quite possibly mis-reading because I didn’t sleep well, ironic, given the topic at hand. All of which is to say it might be my fault for not understanding). – TJ Luoma Dec 1 '19 at 18:43
  • @TJLuoma: Forget the "run every minute scenario" for now, assume he schedules for only once a day. Re-read the two "bullet points" at the top of the answer. The OP responded to my cmt/question above with, "If it can run once a day, that would be useful." Assuming the mac is awake at the sched time, OR wakes up once during the day after the sched time, this will work. As far as your question, pls see next comment :) – Seamus Dec 1 '19 at 19:10
  • @TJLuoma: Wrt "what happens if the mac sleeps twice a day?" That would depend upon when the sleep occurred relative to the scheduled time for the event. Perhaps the best explanation is in man launchd.plist (quoted in the answer above): "If multiple intervals transpire before the computer is woken, those events will be coalesced into one event upon wake from sleep." In other words, if 50 scheduled runs were missed, only one will run to make up for that. – Seamus Dec 1 '19 at 19:45
4

There is no way to do “run on wake” without 3rd-party software. launchd does not support this functionality natively, so any solution that tries to use launchd will be highly likely to fail.

Having said that, there are several good 3rd party options.

SleepWatcher, mentioned by Edd Growl is the only free solution that I am aware of. It has not been updated in a long time, but as far as I know, it still works.

Power Manager which was also mentioned above is an incredibly powerful and capable tool. I wanted to echo the recommendation for that app since I am not involved with its development, but am just a happy paying customer. (I can also say that Power Manager’s developer is highly responsive.)

Keyboard Maestro can run scripts on wake, or sleep, and probably hundreds of other things. It’s such a versatile tool that I recommend every serious Mac owner should own a copy. (It can replace at least a dozen other utilities.) Keyboard Maestro is one of the first apps that I have to install on a new Mac or else it just feels broken. This is the solution that I recommend. Power Manager is a close second.


Edited to add: If you want to run something once-per-day, then launchd is a good solution, and it’s free -- although the syntax is difficult to I would recommend LaunchControl.

7
  • "... no way to do this without 3rd party software" ??!! I beg to differ. – Seamus Dec 1 '19 at 19:47
  • Well, there’s no way to do “at wake” without 3rd party. You can do once-a-day without it. I have updated my answer accordingly. – TJ Luoma Dec 1 '19 at 21:14
  • launchd will do it. The only caveat is that the mac must be asleep at the scheduled time. But I get your point (I think) that if there was something you wanted done only when you woke from sleep, then launchd might not handle that. That said, I'm hard pressed to imagine what would need doing only after sleep - but no doubt there are such requirements. – Seamus Dec 1 '19 at 21:14
  • Yeah, I don’t consider what launchd will do as having “at wake” functionality just because you can set it up to run at a certain time and if your Mac is asleep at that time then it will do it on wake. Those are different things. The only way they happen to overlap is by accident, not intention. I might sleep/wake my Mac 5 times in an hour, but there’s no way I’m going to set something I only want to happen “on wake” to happen 5 times every hour. This truly apples and oranges where someone says “Hey this apple kind of tastes like an orange!" – TJ Luoma Dec 1 '19 at 21:54
  • 1
    Right. And, of course, if you have something that runs every time your Mac wakes up, and your sleeps multiple times a day, one of the 3rd party solutions that runs “on wake” would run the script multiple times a day. “Run once a day” and “Run on wake” and “run no more than once per day” really are different problems. But hopefully someone coming across this thread has sufficient info about how to do that now. – TJ Luoma Dec 2 '19 at 3:55
4

This functionality of running script after waking up can be achieved with the powerful hammerspoon:

function printf(s,...)  print(s:format(...)) end
wather = hs.caffeinate.watcher.new(function(eventType)    
    -- screensDidWake, systemDidWake, screensDidUnlock
    if eventType == hs.caffeinate.watcher.systemDidWake then
        local output = hs.execute("/bin/echo -n hello", false)
        hs.notify.new({title="TestTitle", informativeText=output}):send()
        printf("%s, world", output)
    end
end)
wather:start()

Put those script to $HOME/.hammerspoon/init.lua and reload hammerspoon, and you can check the above /bin/echo output in the hammerspoon console.
There are several wake/sleep events, including screensDidWake, systemDidWake, screensDidUnlock, screensDidSleep, systemWillSleep. See hs.caffeinate.watcher api doc for details.

3

I use a program named EventScripts which among the events it monitors, there is the Computer wakes event. It uses AppleScript scripts to preform the action(s) you want to have happen when a given event is triggered.

You said "I'm looking for a method to run a script (well, a single shell command) when my laptop wakes from sleep.", well you can execute a shell command using the AppleScript do shell script command.


It is a paid app, but does have a demo version you can download to test with before purchasing.


Note: Other then being a paid user of this app, I have no affiliation with its developer.

2

You could install SleepWatcher via homebrew and set a .wake code with your script that will launch every time your laptop wakes from sleep.

To use SleepWatcher: make a shell script in your home directory named .wake and give it permission with the command chmod +x ~/.wake.

You can find out how to use SleepWatcher here

1

Power Manager

DssW Power Manager is able to run scripts on wake. Scripts can be run as a specific user or within the active graphical user's session.

Mentioned in the original accepted answer eight years ago, Power Manager has been developed and supported since 1997.

I work on Power Manager; so feel free to ask technical questions about how it works.

Power Manager on macOS

2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .