Sync directories with CCC
Last modification: Wednesday, December 02, 2009 03:31 pmThis script will let you easily compose and then run an rsync script in the Terminal. Make sure that you have the latest version of Carbon Copy Cloner installed, since the application’s rsync executable is needed for this script to work.
If you didn’t do so already, please visit Mike Bombich at http://www.bombich.com and consider making a donation for his work on Carbon Copy Cloner and the Mac-compatible version of rsync.
Implementation
on run-- Compose the command
set _rsyncCommand to composeRsyncCommand()
if _rsyncCommand is false then return false
-- Run it in the Terminal
tell application "Terminal" to do script _rsyncCommand
end run
on composeRsyncCommand()
-- Get the posix path to the rsync executable
set _rsyncPosixPath to rsyncPosixPath()
if _rsyncPosixPath is false then return false
-- Let the user select source and destination directories
set _posixPaths to chooseSourceAndDestinationPosixPaths()
if _posixPaths is false then return false
set _sourcePosixPath to item 1 of _posixPaths
set _destinationPosixPath to item 2 of _posixPaths
-- Let the user choose from the list of selected rsync parameters
set _parameters to composeRsyncParameters()
if _parameters is false then return false
-- Let the user decide wether to run rsync as root user
set _adminPrivileges to askAboutAdministrativePrivileges()
-- Compose the rsync command
set _rsyncCommand to (quoted form of _rsyncPosixPath) & " " & _parameters & " " & (quoted form of _sourcePosixPath) & " " & (quoted form of _destinationPosixPath)
if _adminPrivileges then set _rsyncCommand to "sudo " & _rsyncCommand
-- Let the user make modifications to the command
set _rsyncCommand to displaySummary(_sourcePosixPath, _destinationPosixPath, _rsyncCommand)
return _rsyncCommand
end composeRsyncCommand
on displaySummary(_sourcePosixPath, _destinationPosixPath, _rsyncCommand)
-- Display a dialog to the user informing them what is going to happen.
activate
set _prompt to "Ready to transfer data from" & return & return & _sourcePosixPath & return & return & "to" & return & return & _destinationPosixPath & return & return & "by running the following command. Changes you make to this command now will alter rsync’s behavior."
set _result to display dialog _prompt default answer _rsyncCommand buttons {"Quit", "Run"} default button "Run"
if button returned of _result is "Quit" then
return false
else if text returned of _result is "" then
return false
else
return text returned of _result
end if
end displaySummary
on askAboutAdministrativePrivileges()
-- Ask the user about the privileges rsync should run with
activate
set _prompt to "Would you like to run rsync with adminitrative privileges?"
set _buttons to {"Standard privileges", "Administrative privileges"}
set _button to button returned of (display dialog _prompt buttons _buttons default button 2 with icon 1)
if _button is "Standard privileges" then
return false
else
return true
end if
end askAboutAdministrativePrivileges
on chooseSourceAndDestinationPosixPaths()
-- Ask the user to select the source and destination directories
repeat
set _sourcePosixPath to chooseDirectoryAsPosixPathWithPrompt("Please select the source directory:")
if _sourcePosixPath is false then return false
set _destinationPosixPath to chooseDirectoryAsPosixPathWithPrompt("Please select the destination directory:")
if _destinationPosixPath is false then return false
-- Return the user’s choices if the paths are not one and the same
if _sourcePosixPath is not _destinationPosixPath then
return {_sourcePosixPath, _destinationPosixPath}
end if
-- Display an error dialog to the user, because the paths are the same.
activate
set _prompt0 to "The source and destination reference the same directory."
set _prompt1 to "If you’re trying to select two volumes that use the same name, you’ll have to rename one of them."
set _prompt to _prompt0 & return & return & _prompt1
set _buttons to {"Quit", "Select again"}
set _button to button returned of (display dialog _prompt buttons _buttons default button 2 with icon 2)
if _button is "Quit" then return false
end repeat
end chooseSourceAndDestinationPosixPaths
on chooseDirectoryAsPosixPathWithPrompt(_prompt)
-- Ask the user for a directory
try
activate
set _location to POSIX file "/Volumes/"
set _chosenFolder to choose folder with prompt _prompt default location _location
-- Convert the alias to a posix path
set _path to aliasToPosixPath(_chosenFolder)
return _path
on error
return false
end try
end chooseDirectoryAsPosixPathWithPrompt
on aliasToPosixPath(_alias)
-- Check wether there's a volume with the same name as the one containing the alias
set _hfsPath to (_alias as string)
set _prvDlmt to text item delimiters
set text item delimiters to ":"
set _foldersDiskName to text item 1 of _hfsPath
set text item delimiters to _prvDlmt
tell application "System Events" to set _diskNames to name of disks
set _sameDiskCount to 0
repeat with _i from 1 to count of _diskNames
if (item _i of _diskNames) is _foldersDiskName then set _sameDiskCount to _sameDiskCount + 1
end repeat
-- Let the user resolve the name conflict
if _sameDiskCount > 1 then
tell application "System Events" to set _diskMountPoints to POSIX path of disks
set _listPrompt to "Please select the mount point for the disk containing the chosen directory:"
activate
set _chosenMountPoint to choose from list _diskMountPoints with prompt _listPrompt default items {item 1 of _diskMountPoints} without multiple selections allowed
if _chosenMountPoint is false then return false
set _chosenMountPoint to item 1 of _chosenMountPoint
if _chosenMountPoint is "/" then set _chosenMountPoint to ""
tell application "System Events" to set _wrongMountPoint to POSIX path of disk _foldersDiskName
set _wrongPosixPath to POSIX path of (_alias as string)
set _prvDlmt to text item delimiters
set text item delimiters to _wrongMountPoint
set _remainingPath to text items 2 thru -1 of _wrongPosixPath as string
set text item delimiters to _prvDlmt
return _chosenMountPoint & "/" & _remainingPath
else
return POSIX path of (_alias as string)
end if
end aliasToPosixPath
on composeRsyncParameters()
-- Specify a list of parameters that rsync supports
set _parameters to {}
set end of _parameters to {"-q", "suppress non-error messages"}
set end of _parameters to {"-h", "output numbers in a human-readable format"}
set end of _parameters to {"-r", "recurse into directories"}
set end of _parameters to {"-l", "copy symlinks as symlinks"}
set end of _parameters to {"-p", "preserve permissions"}
set end of _parameters to {"-o", "preserve owner (super-user only)"}
set end of _parameters to {"-g", "preserve group"}
set end of _parameters to {"-N", "preserve create times"}
set end of _parameters to {"-t", "preserve modification times"}
set end of _parameters to {"-H", "preserve hard links"}
set end of _parameters to {"-A", "preserve ACLs"}
set end of _parameters to {"-X", "preserve extended attributes"}
set end of _parameters to {"-x", "don't cross filesystem boundaries"}
set end of _parameters to {"--devices", "preserve device files (super-user only)"}
set end of _parameters to {"--specials", "preserve special files"}
set end of _parameters to {"--delete", "delete extraneous files from destination dirs"}
set end of _parameters to {"--fileflags", "preserve file-flags (aka chflags)"}
set end of _parameters to {"--force-change", "affect user-/system-immutable files/dirs"}
-- Convert the list above to something more suitable for the choose form list command
set _listTitles to {}
set _defaultTitles to {}
repeat with _i from 1 to count of _parameters
if item 2 of item _i of _parameters does not contain "delete" then
set end of _defaultTitles to item 2 of item _i of _parameters
end if
set end of _listTitles to item 2 of item _i of _parameters
end repeat
-- Ask the user which parameters should be included in the commmand
activate
set _chosenParameters to choose from list _listTitles default items _defaultTitles with multiple selections allowed
if _chosenParameters is false then return false
-- Produce a string of all the chosen parameters
set _combinedParameters to ""
repeat with _i from 1 to count of _parameters
if (item 2 of item _i of _parameters) is in _chosenParameters then
set _combinedParameters to _combinedParameters & (item 1 of item _i of _parameters) & " "
end if
end repeat
return _combinedParameters
end composeRsyncParameters
on rsyncPosixPath()
-- Get the path to the Carbon Copy Cloner application
set _cccPosixPath to carbonCopyClonerPosixPath()
if _cccPosixPath is false then return false
-- Get the path to the rsync executable inside CCC’s application bundle
set _rsyncPosixPath to findRsyncExecutableAtPosixPath(_cccPosixPath)
if _rsyncPosixPath is false then
activate
display dialog "This script does not work with the currently installed version of Carbon Copy Cloner." buttons {"Quit"} default button 1 with icon 0
return false
end if
return _rsyncPosixPath
end rsyncPosixPath
on findRsyncExecutableAtPosixPath(_posixPath)
-- Given a starting directory, find a file named ‘rsync’
try
set _result to do shell script "find " & quoted form of _posixPath & " -name rsync"
if _result is "" then
return false
else
return paragraph 1 of _result
end if
on error
return false
end try
end findRsyncExecutableAtPosixPath
on carbonCopyClonerPosixPath()
try
-- Check default locations for rsync executable
set _rsyncPath to findRsyncExecutableAtPosixPath("/Applications/Carbon Copy Cloner.app/")
if _rsyncPath is not false then return _rsyncPath
set _rsyncPath to findRsyncExecutableAtPosixPath("/Applications/Utilities/Carbon Copy Cloner.app/")
if _rsyncPath is not false then return _rsyncPath
set _homeFolderPath to path to home folder as string
set _rsyncPath to findRsyncExecutableAtPosixPath(POSIX path of (_homeFolderPath & "Applications:Carbon Copy Cloner.app"))
if _rsyncPath is not false then return _rsyncPath
set _rsyncPath to findRsyncExecutableAtPosixPath(POSIX path of (_homeFolderPath & "Downloads:Carbon Copy Cloner:Carbon Copy Cloner.app"))
if _rsyncPath is not false then return _rsyncPath
set _rsyncPath to findRsyncExecutableAtPosixPath(POSIX path of (_homeFolderPath & "Downloads:Carbon Copy Cloner.app"))
if _rsyncPath is not false then return _rsyncPath
set _rsyncPath to findRsyncExecutableAtPosixPath(POSIX path of (_homeFolderPath & "Desktop:Carbon Copy Cloner.app"))
if _rsyncPath is not false then return _rsyncPath
(*
-- Perform a Spotlight search to find Carbon Copy Cloner
-- ### This feature needs a timeout. Runs too long on drives with lots of data.
set _cccPosixPath to item 1 of performSpotlightSearchWithQuery("\"kMDItemFSName == 'Carbon Copy Cloner.app'\"")
*)
error 1
on error
try
-- Ask the user where Carbon Copy Cloner is
activate
set _prompt to "Where is Carbon Copy Cloner?"
set _type to {"com.apple.application-bundle"}
set _location to POSIX file "/Volumes/"
set _ccc to choose file with prompt _prompt of type _type default location _location
return POSIX path of (_ccc as string)
on error
return false
end try
end try
end carbonCopyClonerPosixPath
on performSpotlightSearchWithQuery(_query)
-- Perform a Spotlight search using the specified query and return a list of posix paths
try
set _result to do shell script "mdfind -0 " & _query
on error
return {}
end try
set _prvDlmt to text item delimiters
set text item delimiters to (ASCII character 0)
set _foundPaths to text items of _result
set text item delimiters to _prvDlmt
if item -1 of _foundPaths is "" then
return items 1 thru -2 of _foundPaths
else
return _foundPaths
end if
end performSpotlightSearchWithQuery