Backing up and syncing files and directories between drives is pretty common use case for many users. In this quick tutorial you will learn how to use launchd and rsync to synchronize files between different volumnes in macOS.
Synchronize a (KeePass) file between two drives in macOS in both directions.
- Watch for file changes in both locations
- Synchronize the file between locations and update only if the source file is newer than the destination file
Watch for file changes with
The easiest way to watch for a file or directory for a change is by using launchd. launchd is a macOS service for starting, stopping and managing daemons, applications, processes, and scripts. launchd uses property list (plist) files to configure how the program is to be run. In this scenario we want to run the program whenever the file or directory changes.
The configuration is as follows:
<?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>file.sync</string> <key>ProgramArguments</key> <array> <string>/Users/kolorobot/.scripts/file.sync.sh</string> </array> <key>WatchPaths</key> <array> <string>/Volumes/Kolorodisk/My Files/KeePass/my-db.kdbx</string> <string>/Users/kolorobot/KeePass/my-db.kdbx</string> </array> <key>StandardErrorPath</key> <string>/tmp/file.sync.err</string> <key>StandardOutPath</key> <string>/tmp/file.sync.out</string> </dict> </plist>
The above configuration can be read as follows:
- The name of the of the job is
- The program to be run is
- The program is to be run when there are changes to two files:
- Standard output is redirected to
- Standard error is redirected to
Note no escape character is used in path with
Executed the job on behalf of the currently logged in user
launchd can execute jobs as a system user or a currently logged in user. We will use the second scenario and for that:
- Store the file in
- In terminal, execute
launchctl load -w ~/Library/LaunchAgents/file.sync.plistto load the definition.
To unload the definition run:
launchctl unload -w ~/Library/LaunchAgents/file.sync.plist
Syncing the files
To sync the files we will use
rsync but it will be launched via previously mentioned
The contents of this file:
#!/bin/sh SRC="/Volumes/Kolorodisk/My Files/KeePass/my-db.kdbx" DST="/Users/kolorobot/KeePass/my-db.kdbx" if [ -d /Volumes/Kolorodisk ] then echo "---- Sync ----" rsync -rtuv "$SRC" "$DST" rsync -rtuv "$DST" "$SRC" /usr/bin/osascript -e 'display notification "File sync complete!" with title "Sync"' fi
file.sync.shand make it executable
- Paste the above contents and verify the script is working by executing it
What does the script do?
- It checks if the external drive is mounted
- It synchronizes the file in both directions using rsync. The file is only copied if the source is newer than destination
- It displays the system notification
Full Disk Access for
With macOS Catalina launch daemons and launch agents introduce new user privacy protections (https://developer.apple.com/documentation/macos_release_notes/macos_catalina_10_15_release_notes) and to keep it short, programs run by launchd need to be granted access to the external drive. If not done properly, you may see the following error:
sync: send_files failed to open "/Volumes/Kolorodisk/My Files/KeePass/my-db.kdbx": Operation not permitted (1) rsync error: some files could not be transferred (code 23) at /BuildRoot/Library/Caches/com.apple.xbs/Sources/rsync/rsync-54/rsync/main.c(996) [sender=2.6.9]
The easiest way to fix this is by giving
bin/sh Full Disk Access in
System Preferences > Security & Privacy > Privacy > Full Disk Access.
Test the solution
When all is setup, you can test the solution by making changes to the synchronized files.
Published on System Code Geeks with permission by Rafal Borowiec, partner at our SCG program. See the original article here: macOS: sync files between two volumes using launchd and rsync
Opinions expressed by System Code Geeks contributors are their own.