Having benefited from this site many times, here is my first contribution.
Apple provides an example of how to automatically back up things using a Folder Action. This solution did not meet my needs, however, since:
- It doesn't work with FileVault turned on.
- It always copies everything in the set, whether or not is has changed since the last run.
- It only runs once when the backup disk is mounted.
- Continually creates a backup copy of all files in the backup set when and while a certain external medium is mounted.
- Updates only changed files.
- Runs in the background.
- Looks in one directory for files to be backed up.
- Follows aliases.
- Uses Spotlight to find additional files to back up.
This may not be pretty code, but it works for me and also is quite flexible. The core is the following shell script:
#!/bin/bash
# This is ~/bin/autobackup
# All files matching this spotlight query will be backed up
# This example means: Everything that has a color label
QUERY="kMDItemFSLabel != '0'"
# Check if destination mounted and target folder available. If not, exit.
# DEST_FOLDER and BACKUP_FOLDER environment vars are passed to this script by launchd.
if [ ! -d $DEST_FOLDER ]; then exit 0; fi
# Find all items in BACKUP_FOLDER and pass them to 'autobackupitem.pl' perl scriptfind "$BACKUP_FOLDER" -regex "$BACKUP_FOLDER/[^/]*" -print0 | xargs -0 -n 1 -J/// $HOME/bin/autobackupitem.pl /// $DEST_FOLDER 2>/dev/null
# Find files matching $QUERY and pass them to 'autobackupitem.pl' perl script
mdfind -0 "$QUERY" | xargs -0 -n 1 -J/// $HOME/bin/autobackupitem.pl /// $DEST_FOLDER 2>/dev/null
exit 0I want to follow aliases in $BACKUP_DIR to files I want to include in the backup set, so I need to use some Perl tricks to de-reference the alias. This is the Perl script called from the shell script above:
#!/usr/bin/perl
# This is ~/bin/autobackupitem.pl
# Thanks to http://use.perl.org/~pudge/journal/10437
use Mac::Errors;
use Mac::Files;
use Mac::Resources;
# Source path (may be an Alias)
my $path = $ARGV[0];
# Where to copy it
my $dest = $ARGV[1];
# If $path is an Alias, dereference it
my $link = $path;
my $alis;
my $res = FSpOpenResFile($path, 0) or die $Mac::Errors::MacError;
# get resource by index; get first "alis" resource
my $alis = GetIndResource('alis', 1) or die $Mac::Errors::MacError;
if (! $?){
$link = ResolveAlias($alis);
}
# Use rsync to actually copy it. Filenames may contain blanks, so use escaped quotes.
END { system "rsync -avuE --inplace \"$link\" \"$dest\""; }Finally, in order to call ~/bin/autobackup, we need to configure launchd. Create a file containing the following code, and place it as Autobackup.plist in ~/Library/LaunchAgents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>BACKUP_FOLDER</key>
<string>/Users/ar/Auto Backup</string>
<key>DEST_FOLDER</key>
<string>/Volumes/CFBackup/Backup</string>
<key>HOME</key>
<string>/Users/ar</string>
</dict>
<key>GroupName</key>
<string>ar</string>
<key>Label</key>
<string>de.nvmr.autobackup</string>
<key>Nice</key>
<integer>12</integer>
<key>OnDemand</key>
<true/>
<key>Program</key>
<string>bin/autobackup</string>
<key>RunAtLoad</key>
<false/>
<key>ServiceDescription</key>
<string>Backup flagged files to external medium</string>
<key>StartInterval</key>
<integer>600</integer>
<key>UserName</key>
<string>ar</string>
<key>WatchPaths</key>
<array>
<string>/Volumes</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/ar</string>
</dict>
</plist>I use Lingon to create the launchd configuration and run it.

