Bash script automation in macOS
Scheduling bash scripts to be run at login using `launchd`
Photo by Lucas Hoang on Unsplash
Introduction
There are many scenarios in which bash scripts can be used to automate processes. Sometimes, I like to test things in Python. For instance, sometimes one of my colleagues asks me if something is possible in Python or how it is done. In those cases, I don't want to create a new project, but rather to create a new folder, throw a bunch of python files in, and figure out things. After some time, these test projects make a big mess in my directories, which I don't like.
To fix this, I made a bash script to remove all the files and folders that I have created under "temp" every time I boot my Mac. This blog post will describe how I managed to use macOS launchd
to schedule a bash script to delete all the test files and folders.
What is launchd
?
As a Linux user or at least someone who has worked with Linux, you may be familiar with systemd
. launchd
is exactly the same, but it can do more like a job scheduler (corn replacement) and it is much more reliable than systemd
.
1. How to interface launchd
?
Anyhow, to interact with Launchd, use launchctl
in your bash shell. Same as the systemd
and the systemctl
commands, isn't it? You can see more information about launchctl
by typing man launchctl
in your terminal window.
2. What launchd
configuration files look like?
Having learned about the concept of launchd
and its interface launchctl
, it is now time to understand how we can define a new configuration for this. In contrast to systemd
, which uses TOML config files, launchd
makes use of plist
files to define a configuration. Below is 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>com.example.hello</string>
<key>ProgramArguments</key>
<array>
<string>hello</string>
<string>world</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
3. Where to save launchd
configuration files?
There are three locations in which you can store a launchd
configuration file. If your application is supposed to run as a daemon service, you can store it under /System/Library/LaunchDaemons/
. If your application is supposed to run whenever a user logs in, you should store it under /Library/LaunchAgents
. Finally, If your application is supposed to run whenever a specific user logged in it must be stored under ~/Library/LaunchAgents
.
Creating the automation
It's time to set up the automation now that we know how launchd
works. Let's create the bash script file first:
rm -rf $HOME/projects/temp && mkdir $HOME/projects/temp
You need to save this script to ~/.scripts/loginscript.sh
. The script will delete the files and folders in the temp
folder every time you run it. We will now need to create the config file for Launchd so the script runs every time we log in.
<?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.loginscript</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>PATH-TO-HOME-DIRECTORY/.scripts/loginscript.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Let's save this to ~/Library/LaunchAgents/com.user.loginscript.plist
. Before registering it to launchd
we first need to get the user id as it needs by launchd
:
id -u
Copy the output and run the command below to register the configuration to launchd
:
sudo launchctl bootstrap gui/<USER-ID> ~/Library/LaunchAgents/com.user.loginscript.plist