The problem: creating "TimeMachine-like" snapshots of changed files to a NAS device that is not TimeMachine-compatible.
Caveats with this solution: file permissions will not be maintained in the backup copies.
Overview: uses find and rsync in a shell script called with a launchd daemon.
The script uses a combination of find and rsych to find files altered in the last four hours and to copy them to a NAS device with the directory structure intact. One very slight drawback is the whole path is recreated including /Volumes/[drivename]/...etc.
#!/bin/bash #create a folder to mount the NAS share to. If the folder already exists, this will do nothing mkdir /Volumes/Backup #mount the NAS share to the above folder. Replace NASxxx as appropriate for your configuration mount -t smbfs //NASuser:NASpass@NASaddress/NASsharename /Volumes/Backup #this loop will maintain 20 snapshots numbered 0 to 19, suitable for four daily snapshots, five days a week if [ -d /Volumes/Backup/snapshot.19 ] ; then rm -rf /Volumes/Backup/snapshot.19 fi for i in {18..0} do j=$[i+1] if [ -d /Volumes/Backup/snapshot.$j ] ; then rm -rf /Volumes/Backup/snapshot.$j fi mv /Volumes/Backup/snapshot.$i /Volumes/Backup/snapshot.$j done #using find to scour various paths for files changed within the last four hours and rsync #them to the mounted NAS find /Volumes/DRIVE1/PATH1/ -mmin -360 -type f -exec rsync -aR {} /Volumes/Backup/snapshot.0/ \; find /Volumes/DRIVE1/PATH2/ -mmin -360 -type f -exec rsync -aR {} /Volumes/Backup/snapshot.0/ \; find /Volumes/DRIVE2/PATH1/ -mmin -360 -type f -exec rsync -aR {} /Volumes/Backup/snapshot.0/ \;
I will not be covering how exactly to create a launchd plist file, there is plenty of documentation for creating launch daemons and agents. What I found lacking, however was how to create a launchd plist to run a script at specific intervals. I found plenty of stuff on running at regular intervals, say every X minutes, but nothing good on how to launch at specific times, in this case 8 a.m., 12 p.m., 4 p.m. and 8 p.m. every Monday through Friday, i.e. the launchd plist equivalent to a cron task as such:
0 8,12,16,20 * * 1-5 /usr/local/bin/snapshot.sh
So here is the equivalent launchd plist (and, as a sarcastic aside--Gee, thanks, Apple. This is so much simpler and straightforward.) This is set to run using the "administrator" account. It is the <array> of <dict> that defines the individual run times.
<?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>AbandonProcessGroup</key> <true/> <key>Debug</key> <true/> <key>Label</key> <string>local.snapshot</string> <key>Program</key> <string>/usr/local/bin/snapshot.sh</string> <key>RunAtLoad</key> <false/> <key>StartCalendarInterval</key> <array> <dict> <key>Hour</key> <integer>8</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>1</integer> </dict> <dict> <key>Hour</key> <integer>12</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>1</integer> </dict> <dict> <key>Hour</key> <integer>16</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>1</integer> </dict> <dict> <key>Hour</key> <integer>20</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>1</integer> </dict> <dict> <key>Hour</key> <integer>8</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>2</integer> </dict> <dict> <key>Hour</key> <integer>12</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>2</integer> </dict> <dict> <key>Hour</key> <integer>16</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>2</integer> </dict> <dict> <key>Hour</key> <integer>20</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>2</integer> </dict> <dict> <key>Hour</key> <integer>8</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>3</integer> </dict> <dict> <key>Hour</key> <integer>12</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>3</integer> </dict> <dict> <key>Hour</key> <integer>16</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>3</integer> </dict> <dict> <key>Hour</key> <integer>20</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>3</integer> </dict> <dict> <key>Hour</key> <integer>8</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>4</integer> </dict> <dict> <key>Hour</key> <integer>12</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>4</integer> </dict> <dict> <key>Hour</key> <integer>16</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>4</integer> </dict> <dict> <key>Hour</key> <integer>20</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>4</integer> </dict> <dict> <key>Hour</key> <integer>8</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>5</integer> </dict> <dict> <key>Hour</key> <integer>12</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>5</integer> </dict> <dict> <key>Hour</key> <integer>16</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>5</integer> </dict> <dict> <key>Hour</key> <integer>20</integer> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>5</integer> </dict> </array> <key>UserName</key> <string>administrator</string> </dict> </plist>