Context-sensitive scripting on macOS
In last week’s Under the Radar, Marco and David expounded on the idea of second locations, something I am very familiar with as a frequent and reluctant resident of Laptop Land™. As old as it makes me feel (And I’m way too young to be saying that), I’m simply more productive in front of a desktop-like environment than hunched over a laptop. I also whole-heartedly agree with the idea that trying to bend a laptop into a desktop tends to give you the worst of both worlds. That being said, having a device with full-fledge operating system with me wherever I go has saved my skin too many times to count (Sorry Federico), and with a little scripting and the help of WatchPaths in launchd, I can make my MacBook behave more like a desktop when it’s docked at a desk, and behave like a MacBook when it’s not.
Below is a template for a generic launchd process that runs a script whenever a given file or folder is modified.
<?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.name.of.process</string> <key>ProgramArguments</key> <array> <string>/path/to/script</string> </array> <key>WatchPaths</key> <array> <string>/path/to/file/or/folder/to/watch</string> </array> <key>KeepAlive</key> <true/> </dict> </plist>
For example, as a wallpaper connoisseur, I use and can’t recommend Wallcat enough. It offers a fresh, curated wallpaper every day, with multiple channels to choose from to suite your taste. Another symptom of being a wallpaper connoisseur, I appreciate continuity across desktops when using multiple displays, so I wrote a short appleScript wrapped in a bash script that detects if my MacBook is connected to the dock at my desk, and quits Wallcat (Which is very *ahem* adamant, that its wallpaper is the one showing) and sets the wallpaper appropriately. The script is ran whenever a change to the display pane in System Profiler is detected. Is this the most elegant solution? No, but it is effective.
#!/bin/bash externalDisplays=$(system_profiler SPDisplaysDataType | grep -c 'Name of displays') if [ "$externalDisplays" -eq 2 ] then osascript <<EOF quit application "Wallcat" tell application "System Events" tell desktop 1 to set picture to "path/to/wallpaper/left.jpg" tell desktop 2 to set picture to "path/to/wallpaper/right.jpg" end tell EOF else osascript <<EOF if application "Wallcat" is not running then tell application "Wallcat" to activate end if EOF fi
Once you’ve written your script and launchd plist, you’ll need to add the plist as either a LaunchDaemon or LaunchAgent. LaunchDaemons are loaded and can run during boot, while LaunchAgents are loaded at login, and can be user-specific. With that in mind, I made my wallpaper switcher a LaunchAgent local to my user account, seeing how it relies on files in my Pictures folder. LaunchDaemon plists live in /Library/LaunchDaemons
, LaunchAgents live in /Library/LaunchAgents
, and, shockingly, user-specific LaunchAgents are stored in ~/Library/LaunchAgents
. Make sure LaunchDaemons and non-user-specific LaunchAgents are owned by root. All plists belong in wheel, with 644 permissions. To load plists without needing to log out and log back in or restart:
To unload plists:
LaunchDaemons and LaunchAgents are powerful tools for automating tasks and configurations depending on where you are and what you’re doing.