Sorry for late reply.
As for the parameters, yes, AppleScript's alias specifier requires HFS path, which starts with volume name and uses colon (:) for node separator in path.
As for your example, the properties should be like -
property path1 : "ORIGINAL_DRIVE:" -- searching leading partial path of original item of alias file.
property path2 : "BACKUP_DRIVE:" -- replacing leading partial path of original item of alias file.
provided that you are to redirect aliases under disk "BACKUP_DRIVE", which are originally linked to files in disk "ORIGINAL_DRIVE", to files in "BACKUP_DRIVE" and choose disk "BACKUP_DRIVE" in the choose folder dialague. (I added trailing : to volume paths in path1 and path2 so as to let them strictly match volume name in path)
As for your result, I'm not sure what is going on but I'd guess poor Finder is failing to handle such large directory tree. In the script, alias list originally broken to be labeled by gray colour (aa1), alias list with missing new target to be labeled by red colour (aa2) and alias list to be redirected to existing new target (aa3) are mutually exclusive. So if the aliases labeled by red are correctly resolved to new target, it means they had been already linked to new target before the script is run (unless Finder has done alias redirection when requested to get the original item).
Anyway, gray alias file means Finder could not get its original item in the initial scan and red alias file means AppleScript could not build alias object of its new original item's path in reassign() handler. I think the former would indicate Finder is failing to handle large directory tree and the latter would indicate path1 and/or path2 may not be correct.
----
Now let us be free from miserable Finder scripting in AppleScript. 🙂
Here's newly written RubyCocoa script to redirect aliases under specified directory by editing its target path.
Usage e.g.
#!/bin/bash
#
# Provided that rubycocoa script is saved in ~/Desktop/redirect_aliases.rb, this script will let it
# retrieve and resolve each alias file under /Volumes/DISK B,
# build new targt path by replacing '/Volumes/DISK A/' with '/Volumes/DISK B/' in its original target path,
# and replace the original alias file with new alias file linked to new target.
#
# * See comments in source code for more details
#
cd ~/Desktop
chmod u+x redirect_aliases.rb
./redirect_aliases.rb '/Volumes/DISK B' '/Volumes/DISK A/' '/Volumes/DISK B/'
redirect_aliases.rb
#!/usr/bin/ruby -w
#
# file :
# redirect_aliases.rb
#
# ARGV :
# [directory, search, replace]
# directory : POSIX path of directory which target tree is rooted at
# search : search string in alias's target path
# replace : replace string
#
# This script will resolve each alias file under specified <directory>,
# build new target path by replacing <search> string with <replace> string in original target path,
# and replace the original alias file with new alias file linked to new target.
#
# * If original alias file is unresolvable, it is labeled by gray colour.
# * If new target does not exist, the original alias file is preserved and labeled by red colour.
# * Alias resolution is static, i.e., target volume must be mounted in advance.
# * Package contents are not scanned.
#
# version :
# v0.10
#
# written by Hiroto, 2015-09
#
require 'osx/cocoa'
include OSX
begin
URLKEY = NSURLPathKey # 10.8 or later
rescue
URLKEY = NSURLNameKey # 10.6 or later (fallback)
end
def resolve_alias(f, opts = {})
# string f : POSIX path of alias file
# hash opts : {:quiet => boolean}
# :quiet => true to suppress error message, false otherwise; default = false
# return array : [rc, f1]
# rc = return code
# 0 = alias is resolved
# 1 = alias is not resolved but target is retrieved
# 2 = target is not retrieved
# 3 = bookmark data is not obtained (e.g., f is not alias)
# f1 = POSIX path of target file or nil if failed
opts = {:quiet => false}.merge(opts)
url = NSURL.fileURLWithPath(f)
err = OCObject.new
bmrk = NSURL.objc_send(
:bookmarkDataWithContentsOfURL, url,
:error, err)
unless bmrk
$stderr.puts %[%s: failed to get bookmark data - %s] % [f, err] unless opts[:quiet]
return [3, nil]
end
stale = ObjcPtr.new(:char) # BOOL*
err = OCObject.new
url1 = NSURL.objc_send(
:URLByResolvingBookmarkData, bmrk,
:options, NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI,
:relativeToURL, nil,
:bookmarkDataIsStale, stale,
:error, err)
unless url1
$stderr.puts %[%s: failed to resolve alias - %s] % [f, err] unless opts[:quiet]
dict = NSURL.objc_send(
:resourceValuesForKeys, [URLKEY],
:fromBookmarkData, bmrk)
path2 = dict[URLKEY]
unless path2
$stderr.puts %[s: failed to get target path - %s] % [f, err] unless opts[:quiet]
return [2, nil]
else
return [1, path2]
end
end
if stale.bool != 0
$stderr.puts %[%s: stale alias] % f unless opts[:quiet]
end
return [0, url1.path]
end
def create_alias(g, f, opts = {})
# string g : POSIX path of original file (target of alias file)
# string f : POSIX path of alias file
# hash opts : {:quiet => boolean}
# :quiet => true to suppress error message, false otherwise; default = false
# return array : [rc, f1]
# rc = return code
# 0 = alias file is created successfully
# 1 = bookmark data is created but not written to alias file
# 2 = bookmark data is not created (e.g., g does not exist)
# f1 = POSIX path of alias file or nil if failed
opts = {:quiet => false}.merge(opts)
err = OCObject.new
bmrk = NSURL.fileURLWithPath(g).objc_send(
:bookmarkDataWithOptions, NSURLBookmarkCreationSuitableForBookmarkFile,
:includingResourceValuesForKeys, [],
:relativeToURL, nil,
:error, err)
unless bmrk
$stderr.puts %[%s: failed to create bookmark data - %s] % [g, err] unless opts[:quiet]
return [2, nil]
end
err = OCObject.new
url = NSURL.fileURLWithPath(f)
b = NSURL.objc_send(
:writeBookmarkData, bmrk,
:toURL, url,
:options, NSURLBookmarkCreationSuitableForBookmarkFile,
:error, err)
unless b
$stderr.puts %[%s: failed to write bookmark data - %s] % [f, err] unless opts[:quiet]
return [1, nil]
end
return [0, url.path]
end
def scan_aliases(p, opts = {})
# string p : absolute POSIX path of directory or file
# hash opts : {:package => boolean}
# :package => true to scan package contents, false otherwise; default = false
# return array : aliases in directory tree rooted at p if p is directory; [p] if p is alias file; otherwise []
opts = {:package => false}.merge(opts)
aa = []
ws = NSWorkspace.sharedWorkspace
uti = ws.typeOfFile_error(p, nil)
if ['public.folder', 'public.volume'].include?(uti)
de = NSFileManager.defaultManager.enumeratorAtPath(p)
while (n = de.nextObject) != nil do
f = p + '/' + n.to_s
de.skipDescendants if ! opts[:package] && ws.isFilePackageAtPath(f)
if de.fileAttributes.objectForKey(NSFileType) == NSFileTypeRegular
aa << f if ws.typeOfFile_error(f, nil) == 'com.apple.alias-file'
end
end
elsif uti == 'com.apple.alias-file'
aa << p
end
aa
end
def set_label(f, lb, opts = {})
# string f : POSIX path of file or directory
# integer lb : label index
# label index => colour
# 0 => none
# 1 => gray
# 2 => green
# 3 => purple
# 4 => blue
# 5 => yellow
# 6 => red
# 7 => orange
# hash opts : {:quiet => boolean}
# :quiet => true to suppress error message, false otherwise; default = false
# return boolean : true if successful, false otherwise.
opts = {:quiet => false}.merge(opts)
err = OCObject.new
b = NSURL.fileURLWithPath(f).objc_send(
:setResourceValue, NSNumber.numberWithInt(lb),
:forKey, NSURLLabelNumberKey,
:error, err)
unless b
$stderr.puts '%s: failed to set label index to %d - %s' % [f, lb, err] unless opts[:quiet]
end
b
end
def main(argv)
d = File.expand_path(argv.shift)
s, r, = argv
scan_aliases(d).each do |f|
rc, g = resolve_alias(f)
if rc == 0
g1 = g.sub(s, r)
next if g1 == g
if File.exist?(g1)
File.delete(f) # this should not be necessary but is to update Finder's cache assuredly
create_alias(g1, f)
else
# unresolvable redirection (new target does not exist)
$stderr.puts '%s: failed to redirect alias - New target (%s) does not exist.' % [f, g1]
set_label(f, 6)
end
else
# originally unresolvable alias
set_label(f, 1)
end
end
end
main(ARGV)
Notes on RubyCocoa script.
- Under OS X 10.10, you need to manually install RubyCocoa 1.2.0 which supports Ruby 2.0 or later.
https://rubycocoa.github.io/
https://github.com/rubycocoa/rubycocoa/releases
- Under OS X 10.9, unless you have installed RubyCocoa 1.2.0 for Ruby 2.0, specify the ruby interpreter at
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
- Under OS X 10.5 through 10.8, script should work as is.
----
In case, here's an AppleScript wrapper of the script.
--APPLESCRIPT
--set _directory to "/Volumes/DISK B"
set _directory to (choose folder)'s POSIX path
set _search to "/Volumes/DISK A/"
set _replace to "/Volumes/DISK B/"
redirect_aliases(_directory, _search, _replace)
display dialog "Aliases' redirection done"
on redirect_aliases(d, s, r)
(*
string d : POSIX path of directory which target tree is rooted at
string s : search string in alias's target path
string r : replace string
*)
set args to ""
repeat with a in {d, s, r}
set args to args & a's quoted form & space
end repeat
do shell script "/bin/bash -s <<'HUM' - " & args & "
/usr/bin/ruby -w <<'EOF' - \"$@\"
#
# ARGV :
# [directory, search, replace]
# directory : POSIX path of directory which target tree is rooted at
# search : search string in alias's target path
# replace : replace string
#
# This script will resolve each alias file under specified <directory>,
# build new target path by replacing <search> string in original target path with <replace> string,
# and replace the original alias file with new alias file linked to new target.
#
# * If original alias file is unresolvable, it is labeled by gray colour.
# * If new target does not exist, the original alias file is preserved and labeled by red colour.
# * Alias resolution is static, i.e., target volume must be mounted in advance.
# * Package contents are not scanned.
#
# version :
# v0.10
#
# written by Hiroto, 2015-09
#
require 'osx/cocoa'
include OSX
begin
URLKEY = NSURLPathKey # 10.8 or later
rescue
URLKEY = NSURLNameKey # 10.6 or later (fallback)
end
def resolve_alias(f, opts = {})
# string f : POSIX path of alias file
# hash opts : {:quiet => boolean}
# :quiet => true to suppress error message, false otherwise; default = false
# return array : [rc, f1]
# rc = return code
# 0 = alias is resolved
# 1 = alias is not resolved but target is retrieved
# 2 = target is not retrieved
# 3 = bookmark data is not obtained (e.g., f is not alias)
# f1 = POSIX path of target file or nil if failed
opts = {:quiet => false}.merge(opts)
url = NSURL.fileURLWithPath(f)
err = OCObject.new
bmrk = NSURL.objc_send(
:bookmarkDataWithContentsOfURL, url,
:error, err)
unless bmrk
$stderr.puts %[%s: failed to get bookmark data - %s] % [f, err] unless opts[:quiet]
return [3, nil]
end
stale = ObjcPtr.new(:char) # BOOL*
err = OCObject.new
url1 = NSURL.objc_send(
:URLByResolvingBookmarkData, bmrk,
:options, NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI,
:relativeToURL, nil,
:bookmarkDataIsStale, stale,
:error, err)
unless url1
$stderr.puts %[%s: failed to resolve alias - %s] % [f, err] unless opts[:quiet]
dict = NSURL.objc_send(
:resourceValuesForKeys, [URLKEY],
:fromBookmarkData, bmrk)
path2 = dict[URLKEY]
unless path2
$stderr.puts %[s: failed to get target path - %s] % [f, err] unless opts[:quiet]
return [2, nil]
else
return [1, path2]
end
end
if stale.bool != 0
$stderr.puts %[%s: stale alias] % f unless opts[:quiet]
end
return [0, url1.path]
end
def create_alias(g, f, opts = {})
# string g : POSIX path of original file (target of alias file)
# string f : POSIX path of alias file
# hash opts : {:quiet => boolean}
# :quiet => true to suppress error message, false otherwise; default = false
# return array : [rc, f1]
# rc = return code
# 0 = alias file is created successfully
# 1 = bookmark data is created but not written to alias file
# 2 = bookmark data is not created (e.g., g does not exist)
# f1 = POSIX path of alias file or nil if failed
opts = {:quiet => false}.merge(opts)
err = OCObject.new
bmrk = NSURL.fileURLWithPath(g).objc_send(
:bookmarkDataWithOptions, NSURLBookmarkCreationSuitableForBookmarkFile,
:includingResourceValuesForKeys, [],
:relativeToURL, nil,
:error, err)
unless bmrk
$stderr.puts %[%s: failed to create bookmark data - %s] % [g, err] unless opts[:quiet]
return [2, nil]
end
err = OCObject.new
url = NSURL.fileURLWithPath(f)
b = NSURL.objc_send(
:writeBookmarkData, bmrk,
:toURL, url,
:options, NSURLBookmarkCreationSuitableForBookmarkFile,
:error, err)
unless b
$stderr.puts %[%s: failed to write bookmark data - %s] % [f, err] unless opts[:quiet]
return [1, nil]
end
return [0, url.path]
end
def scan_aliases(p, opts = {})
# string p : absolute POSIX path of directory or file
# hash opts : {:package => boolean}
# :package => true to scan package contents, false otherwise; default = false
# return array : aliases in directory tree rooted at p if p is directory; [p] if p is alias file; otherwise []
opts = {:package => false}.merge(opts)
aa = []
ws = NSWorkspace.sharedWorkspace
uti = ws.typeOfFile_error(p, nil)
if ['public.folder', 'public.volume'].include?(uti)
de = NSFileManager.defaultManager.enumeratorAtPath(p)
while (n = de.nextObject) != nil do
f = p + '/' + n.to_s
de.skipDescendants if ! opts[:package] && ws.isFilePackageAtPath(f)
if de.fileAttributes.objectForKey(NSFileType) == NSFileTypeRegular
aa << f if ws.typeOfFile_error(f, nil) == 'com.apple.alias-file'
end
end
elsif uti == 'com.apple.alias-file'
aa << p
end
aa
end
def set_label(f, lb, opts = {})
# string f : POSIX path of file or directory
# integer lb : label index
# label index => colour
# 0 => none
# 1 => gray
# 2 => green
# 3 => purple
# 4 => blue
# 5 => yellow
# 6 => red
# 7 => orange
# hash opts : {:quiet => boolean}
# :quiet => true to suppress error message, false otherwise; default = false
# return boolean : true if successful, false otherwise.
opts = {:quiet => false}.merge(opts)
err = OCObject.new
b = NSURL.fileURLWithPath(f).objc_send(
:setResourceValue, NSNumber.numberWithInt(lb),
:forKey, NSURLLabelNumberKey,
:error, err)
unless b
$stderr.puts '%s: failed to set label index to %d - %s' % [f, lb, err] unless opts[:quiet]
end
b
end
def main(argv)
d = File.expand_path(argv.shift)
s, r, = argv
scan_aliases(d).each do |f|
rc, g = resolve_alias(f)
if rc == 0
g1 = g.sub(s, r)
next if g1 == g
if File.exist?(g1)
File.delete(f) # this should not be necessary but is to update Finder's cache assuredly
create_alias(g1, f)
else
# unresolvable redirection (new target does not exist)
$stderr.puts '%s: failed to redirect alias - New target (%s) does not exist.' % [f, g1]
set_label(f, 6)
end
else
# originally unresolvable alias
set_label(f, 1)
end
end
end
main(ARGV)
EOF
HUM"
end redirect_aliases
--END OF APPLESCRIPT
Tested under OS X 10.6.8 but no warranties of any kind. Please make sure you have backup of directories in advance when running this sort of script. It is good practice to first test it on small test data set.
Good luck,
H