How To Create Services in macOS using $Bash — launchctl, and plutil Commands — Step-by-Step Guide!

Someshwaran M
10 min readDec 15, 2022

Do you want to understand how services are created in macOS? How does your macOS know that you have installed an application? How does it know how long your screen time is ON? All is through background services!!

In this article, we will cover some of the key features of launchctl and how to use it to manage services in macOS. We will discuss how to use launchd and plutil to create and configure services, and how to convert property list files to different formats. By using the tools and techniques described in this article, you can create and manage services in macOS, and automate tasks and scripts to run automatically and efficiently.

That said, let’s dive right into the article, to create a service in macOS using bash, you can use the automator tool to create a new service. The reason why we choose $bash is to directly interact with the kernel of any OS without any built-in libraries.

[How To Create A Service]

  • Open Automator from your Applications folder.
  • Select Service from the list of document types.
  • In the top search bar, type Run Shell Script and double-click the result to add it to the workflow.
  • From here, you can customize the service by writing your desired bash commands in the Run Shell Script action. For example, if you want to create a service that backs up a file or folder to a specified directory, you could use the following $bash script:
#!/bin/bash
# Get the file or folder that the service is being run on
input=$(cat)

# Set the directory where the backup should be saved
backup_dir=~/Desktop/Backups

# Create the backup directory if it does not exist
if [ ! -d "$backup_dir" ]; then
mkdir -p "$backup_dir"
fi

# Create a timestamp for the backup
timestamp=$(date +%Y-%m-%d_%H-%M-%S)

# Create the backup file
cp -R "$input" "$backup_dir/$timestamp-backup"

Once you have finished configuring your service, you can save it and give it a name. This will run the $bash script and create a backup of the file or folder in the specified directory.

To use the service, you can right-click on a file or folder in Finder and select your service from the contextual menu.

To customize the service, you can add additional actions to the workflow by dragging them from the left-hand panel and dropping them into the workflow on the right. For example, you could add an Ask for Finder Items action to prompt the user to select one or more files or folders before running the shell script.

When configuring the Run Shell Script action, you can choose whether to pass input to the script as arguments or as standard input. You can also choose whether to show the output of the script in a new window or to speak the output using the built-in text-to-speech capabilities of macOS.

Once you have finished configuring your service, you can save it by clicking File > Save and giving it a name. The service will be saved to the ~/Library/Services folder and will be available from the contextual menu in Finder. You can also access and manage your services by opening Automator and selecting Services from the top menu.

NOTE: After you have saved your service, you can test it by right-clicking on a file or folder in Finder and selecting your service from the contextual menu. If your service is working correctly, it should run the bash commands that you specified in the run Shell Script action.

One thing to keep in mind when creating services in macOS is that they can only be used to automate tasks that can be performed on a single item at a time. For example, if you want to run a $bash script that processes multiple files, you will need to create a separate service for each file.

Additionally, you can use the automator tool to create services that can be triggered using keyboard shortcuts. To do this, you can add a Launch Application or Run AppleScript action to your workflow and specify the keyboard shortcut that you want to use to trigger the service. This can make it easier to access your services without having to use the contextual menu in Finder.

[launchd]

In addition to using the automator tool to create services, you can also create services using the launchd utility in macOS. This utility allows you to run scripts and other programs automatically at specified intervals or at specific times. To create a service using launchd, you will need to create a property list file (also known as a plist file) that contains the configuration settings for your service.

<?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.backup-service</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>
# Get the file or folder that the service is being run on
input=$(cat)

# Set the directory where the backup should be saved
backup_dir=~/Desktop/Backups

# Create the backup directory if it does not exist
if [ ! -d "$backup_dir" ]; then
mkdir -p "$backup_dir"
fi

# Create a timestamp for the backup
timestamp=$(date +%Y-%m-%d_%H-%M-%S)

# Create the backup file
cp -R "$input" "$backup_dir/$timestamp-backup"
</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
</dict>
</plist>

As far as the above backup service example is concerned, the Label key specifies the unique identifier for your service, and the ProgramArguments key specifies the $bash script that you want to run as your service. Also, the service will run the script every hour (3600 seconds) and create a backup of the selected file or folder.

The property list file should be saved in the ~/Library/LaunchAgents directory and should have a filename in the following format: <label>.plist, where <label> is a unique identifier for your service. The property list file should contain the following keys:

Label: This is the unique identifier for your service. It should match the filename of your property list file, without the .plist extension.
ProgramArguments: This is an array of strings that specifies the command or script that you want to run as your service.
StartInterval: This is an integer that specifies the number of seconds between each time your service is run. This key is only used if you want your service to be run at regular intervals.
StartCalendarInterval: This is a dictionary that specifies the exact date and time when your service should be run. This key is only used if you want your service to be run at a specific time.

To run your service using launchd, you can use the launchctl command to load the property list file that you created. For example, if your property list file is called myService.plist and is saved in the ~/Library/LaunchAgents directory, you can use the following command to load it:

#Command
launchctl load ~/Library/LaunchAgents/myService.plist

Once your property list file is loaded, launchd will automatically run your service according to the configuration settings that you specified in the property list file. You can use the launchctl command to start, stop, and manage your service, as well as view the status of your service and any errors that may have occurred.

In summary, there are two ways to create services in macOS: using the automator tool or using the launchd utility. Both methods allow you to automate tasks and run scripts automatically, but launchd offers more advanced options for scheduling and managing services.

One important thing to keep in mind when using launchd to create services is that your property list file must be in the correct format and must contain all of the required keys. If your property list file is not formatted correctly, launchd will not be able to run your service.

[plutil]

To ensure that your property list file is formatted correctly, you can use the plutil command to validate it. For example, if your property list file is called myService.plist, you can use the following command to validate it:

#Command
plutil -lint myService.plist

If your property list file is valid, plutil will return no output. If there are any errors in your property list file, plutil will display them and provide details about what is wrong and how to fix it. For example, if your property list file is missing the Label key, plutil will output the following error:

#Error
myService.plist: The key 'Label' is missing.

By using the plutil command to validate your property list file, you can ensure that your service is set up correctly and will run without any issues.

In addition to using plutil to validate your property list file, you can also use it to convert your property list file to a different format. By default, property list files in macOS are saved in binary format, but you can use plutil to convert them to XML or JSON format.

To convert a property list file from binary to XML or JSON format, you can use the -convert option with plutil. For example, if you want to convert a property list file called myService.plist from binary to XML format, you can use the following command:

#Command
plutil -convert xml1 myService.plist

If the conversion is successful, plutil will create a new file called myService.xml that contains the same data as the original property list file, but in XML format. You can then edit the XML file using a text editor, and use plutil to convert it back to binary format when you are finished.

Converting property list files to XML or JSON format can be useful if you want to share your property list files with others, or if you want to use a different tool to edit your property list files. By using plutil to convert your property list files, you can ensure that they are in the correct format and will work with launchd and other tools in macOS.

[launchctl]

In addition to using launchd and plutil to manage services in macOS, you can also use the launchctl command to control and manage your services. With launchctl, you can start, stop, and restart your services, as well as view the status of your services and any errors that may have occurred.

To start a service using launchctl, you can use the start subcommand followed by the Label of the service that you want to start. For example, if the Label of your service is com.example.myservice, you can use the following command to start it:

#Command
launchctl start com.example.myservice

If your service is started successfully, launchctl will return no output. If there are any errors, launchctl will display them and provide details about what went wrong. For example, if your service is already running, launchctl will output the following error:

#Response
com.example.myservice: Already loaded

By using launchctl to start your services, you can ensure that they are running and will be able to perform the tasks that you have configured them to do.

In addition to starting your services, you can also use launchctl to stop and restart them. To stop a service using launchctl, you can use the stop subcommand followed by the Label of the service that you want to stop. For example, if the Label of your service is com.example.myservice, you can use the following command to stop it:

#Command
launchctl stop com.example.myservice

If your service is stopped successfully, launchctl will return no output. If there are any errors, launchctl will display them and provide details about what went wrong. For example, if your service is not currently running, launchctl will output the following error:

#Error
com.example.myservice: Could not find specified service

By using launchctl to stop your services, you can ensure that they are not running and will not consume any system resources. This can be useful if you want to temporarily disable a service, or if you want to stop a service before making changes to its configuration.

To restart a service using launchctl, you can use the stop and start subcommands together. For example, if the Label of your service is com.example.myservice, you can use the following command to stop and then restart it:

#Command
launchctl stop com.example.myservice && launchctl start com.example.myservice

This command will first stop the service, and then start it again. By using this command, you can quickly restart a service without having to stop and start it separately. This can be useful if you want to apply changes to a service’s configuration without having to manually stop and start it.

In addition to starting, stopping, and restarting your services, you can also use launchctl to view the status of your services and any errors that may have occurred. To view the status of a service using launchctl, you can use the list subcommand followed by the Label of the service that you want to view. For example, if the Label of your service is com.example.myservice, you can use the following command to view its status:

#Command
launchctl list com.example.myservice

If your service is running and there are no errors, launchctl will display information about the service, including its Label, PID (process ID), and Status. For example, the output of the list subcommand might look like this:

#Response
{
"Label" = "com.example.myservice";
"PID" = 12345;
"Status" = "Running";
}

If there are any errors with your service, launchctl will display them along with the Label of the service. For example, if your service is not currently running, launchctl will output the following error:

#Error
com.example.myservice: Could not find specified service

By using the list subcommand, you can quickly check the status of your services and see if they are running properly. This can be useful if you want to troubleshoot any issues with your services or if you just want to see if they are running as expected.

To conclude, the launchctl is a powerful tool for managing services in macOS. With launchctl, you can start, stop, and restart your services, as well as view the status of your services and any errors that may have occurred. By using launchctl in combination with launchd and plutil, you can easily create and manage services in macOS, and automate tasks and scripts to run automatically at specified intervals or at specific times.

Thanks for reading!! If you enjoyed this article, please follow and subscribe for the latest updates. Looking for more? Check out the other posts below:

--

--

Someshwaran M

I am an Open-Source Enthusiast. I learned a lot from the Open-Source community and I love how collaboration, knowledge sharing happens through Open-Source!