Alias script concept

Mockman,

Thank you! kMDItemContentType returns all aliases and only aliases in my testing!

This version is a bit more polished with feedback on the located broken aliases and a report on the actions taken at the end. It correctly identifies all broken aliases on my boot drive in ~1 second and offers options to re-link, skip or delete broken aliases that it finds.

Hopefully this version works properly for others as well.

--9/8/25 https://www.macscripter.net/u/paulskinner/
--Running under AppleScript 2.8, MacOS 15.5
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
property deletedBrokenAliases : 0
property skippedBrokenAliases : 0
property repairedBrokenAliases : 0

set fileSystemObject to quoted form of POSIX path of (choose folder with prompt "Select a folder to search for broken Aliases.")
set brokenAliasList to Filesystem_Alias_List_Broken(fileSystemObject)
set foundNoticeString to "Found " & length of brokenAliasList & " broken alias files."
if length of brokenAliasList < 20 then
	copy brokenAliasList to brokenAliasNameList
	FileSystem_Objects_Names(brokenAliasNameList)
	set brokenAliasListText to String_Convert_List_To_String(brokenAliasNameList, return)
	set foundNoticeString to foundNoticeString & return & return & brokenAliasListText
end if
display dialog foundNoticeString with title "Located broken aliases"
repeat with thisBrokenAlias in brokenAliasList
	set thisBrokenAliasName to FileSystem_Object_Name_And_Extension(thisBrokenAlias)
	set thisBrokenAliasContainer to FileSystem_Object_Container(thisBrokenAlias)
	set potentialOriginalFiles to Filesystem_Alias_List_Potential_Originals(thisBrokenAliasName)
	if potentialOriginalFiles is not {} then
		set userCancelled to Filesystem_Alias_Choose_New_Original_File(thisBrokenAlias, thisBrokenAliasContainer, thisBrokenAliasName, potentialOriginalFiles)
		if userCancelled then
			display dialog "User cancelled."
			exit repeat
		end if
	else
		set skippedBrokenAliases to skippedBrokenAliases + 1
	end if
end repeat
set conclusionText to "Alias re-linker complete." & return
set conclusionText to conclusionText & return & repairedBrokenAliases & " broken aliases were re-linked to a valid original file." & return
set conclusionText to conclusionText & return & skippedBrokenAliases & " broken aliases were skipped. Either no original files with the same name were found or the user did not select a new original file." & return
set conclusionText to conclusionText & return & deletedBrokenAliases & " broken aliases were deleted." & return
display dialog conclusionText








on Filesystem_Alias_Choose_New_Original_File(thisBrokenAlias, thisBrokenAliasContainer, thisBrokenAliasName, potentialOriginalFiles)
	set canceledState to false
	set potentialOriginalFiles to potentialOriginalFiles & "Don't re-link this alias."
	set potentialOriginalFiles to potentialOriginalFiles & "Delete this alias."
	try
		set chosenAlias to choose from list potentialOriginalFiles with prompt "The listed files have the same name as the broken alias '" & thisBrokenAliasName & "'" & return & return & "Choose a new original file for this alias:" & return & return & thisBrokenAlias & return with title "Choose new original file" default items "Don't re-link this alias."
		if chosenAlias is false then
			set skippedBrokenAliases to skippedBrokenAliases + 1
			return true
		end if
		if chosenAlias is not {} and chosenAlias is not in {"Don't re-link this alias.", "Delete this alias."} then
			set newAlias to FileSystem_Object_Alias_Make(item 1 of chosenAlias, thisBrokenAliasContainer, thisBrokenAliasName)
			set repairedBrokenAliases to repairedBrokenAliases + 1
		else
			if chosenAlias is {"Delete this alias."} then
				FileSystem_Object_Delete(thisBrokenAlias)
				set deletedBrokenAliases to deletedBrokenAliases + 1
			end if
		end if
	end try
	return canceledState
end Filesystem_Alias_Choose_New_Original_File
on Filesystem_Alias_List_Broken(fileSystemObject)
	set ss to "mdfind -onlyin " & fileSystemObject & " " & "kMDItemContentType == \"com.apple.alias-file\""
	set aliasFileList to paragraphs of (do shell script ss)
	set brokenAliasList to {}
	repeat with thisAliasFile in aliasFileList
		set thisAliasFile to contents of thisAliasFile
		try
			FileSystem_Object_Alias_Resolve(thisAliasFile)
		on error e number n
			{e, n}
			if n is -1700 then set the end of brokenAliasList to thisAliasFile
		end try
	end repeat
	return brokenAliasList
end Filesystem_Alias_List_Broken
on Filesystem_Alias_List_Potential_Originals(thisBrokenAliasName)
	--Find potential originalFiles
	thisBrokenAliasName
	set thisBrokenAliasSEARCHName to item 1 of String_Text_Items_Of(thisBrokenAliasName, {" alias"})
	
	set searchShellScript to "mdfind -literal 'kMDItemFSName == \"" & thisBrokenAliasSEARCHName & "\"'"
	set potentialOriginalFiles to paragraphs of (do shell script searchShellScript)
	set finalPotentialAliasOriginalFiles to {}
	repeat with thisPotentialAliasOriginal in potentialOriginalFiles
		set thisPotentialAliasOriginal to contents of thisPotentialAliasOriginal
		set thisPotentialAliasOriginalReference to FileSystem_Convert_Object_To_Alias(thisPotentialAliasOriginal)
		tell application "System Events"
			set thisPotentialAliasOriginalKind to kind of thisPotentialAliasOriginalReference
			if thisPotentialAliasOriginalKind is not in {"Alias"} then
				set the end of finalPotentialAliasOriginalFiles to thisPotentialAliasOriginal
			end if
		end tell
	end repeat
	return finalPotentialAliasOriginalFiles
end Filesystem_Alias_List_Potential_Originals

on FileSystem_Convert_Object_To_Alias(fileSystemObject)
	try
		if class of fileSystemObject is text then --check for a quoted posix path and remove the quoting
			if character 1 of fileSystemObject is "'" and character -1 of fileSystemObject is "'" then set fileSystemObject to text 2 thru -2 of fileSystemObject
		end if
		try
			return fileSystemObject as alias
		on error
			try
				return (POSIX path of fileSystemObject) as alias --handles Posix files
			on error
				return (POSIX file (fileSystemObject as text)) as alias --handles Posix paths
			end try
		end try
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Convert_Object_To_Alias>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Convert_Object_To_Alias
on FileSystem_Convert_Objects_To_Aliases(fileSystemObjectList)
	try
		set fileSystemObjectList to (fileSystemObjectList) as list
		repeat with currentIndex from 1 to length of fileSystemObjectList
			set item currentIndex of fileSystemObjectList to FileSystem_Convert_Object_To_Alias(item currentIndex of fileSystemObjectList)
		end repeat
		return fileSystemObjectList
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Convert_Objects_To_Aliases>" & space & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Convert_Objects_To_Aliases
on FileSystem_Convert_Object_To_NSURL(fileSystemObject)
	try
		set fileSystemObject to FileSystem_Convert_Object_To_PosixPath(fileSystemObject)
		set fileSystemObject to current application's class "NSURL"'s fileURLWithPath:fileSystemObject
		return fileSystemObject
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Convert_Object_To_NSURL>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Convert_Object_To_NSURL
on FileSystem_Convert_Object_To_PosixPath(fileSystemObject)
	try
		try
			set posixPath to POSIX path of fileSystemObject
		on error
			try
				tell application "System Events"
					set posixPath to POSIX path of ((path of disk item (fileSystemObject as string)) as alias)
				end tell
			on error
				set posixPath to POSIX path of (fileSystemObject as alias)
			end try
		end try
		return current application's NSString's stringWithString:posixPath
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Convert_Object_To_PosixPath>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Convert_Object_To_PosixPath

on FileSystem_Object_Alias_Make(fileSystemObject, aliasDestinationFolder, thisAliasName)
	-- Creates an alias file at the provided path
	set originalURL to FileSystem_Convert_Object_To_NSURL(fileSystemObject)
	set originalName to FileSystem_Object_Name_And_Extension(fileSystemObject)
	set targetFolder to FileSystem_Convert_Object_To_NSURL(aliasDestinationFolder)
	set theAliasPath to (targetFolder as text) & thisAliasName
	set theAliasURL to FileSystem_Convert_Object_To_NSURL(theAliasPath)
	set {theData, theError} to originalURL's bookmarkDataWithOptions:1024 includingResourceValuesForKeys:(missing value) relativeToURL:(missing value) |error|:(reference) -- 1024 = NSURLBookmarkCreationSuitableForBookmarkFile
	if theData is missing value then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
	set {theResult, theError} to current application's |NSURL|'s writeBookmarkData:theData toURL:theAliasURL options:0 |error|:(reference)
	if not (theResult as boolean) then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
	return theAliasURL
end FileSystem_Object_Alias_Make
on FileSystem_Object_Alias_Resolve(fileSystemObject)
	set theAliasURL to FileSystem_Convert_Object_To_NSURL(fileSystemObject)
	set the AliasURLString to (theAliasURL) as text
	set {theData, theError} to current application's |NSURL|'s bookmarkDataWithContentsOfURL:theAliasURL |error|:(reference)
	if theData is missing value then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
	set theDict to current application's |NSURL|'s resourceValuesForKeys:{current application's NSURLPathKey} fromBookmarkData:theData
	set thePath to theDict's objectForKey:(current application's NSURLPathKey)
	set originalObject to FileSystem_Convert_Object_To_NSURL(thePath)
	set locatedOriginalNSURL to (FileSystem_Convert_Object_To_NSURL(originalObject)) as text
	if locatedOriginalNSURL is AliasURLString then
		error "alias self-resolves."
	end if
end FileSystem_Object_Alias_Resolve
on FileSystem_Object_Container(fileSystemObject)
	--https://www.macscripter.net/u/Fredrik71
	--https://macscripter.net/viewtopic.php?id=49262
	--Fails with filenames containing multiple period characters.
	try
		set posixPathOfTheFile to FileSystem_Convert_Object_To_PosixPath(fileSystemObject)
		set sourceText to current application's NSString's stringWithString:posixPathOfTheFile
		set FileSystem_ObjectParentFolder to (sourceText's stringByDeletingLastPathComponent()) as string
		set FileSystem_ObjectParentFolder to FileSystem_Convert_Object_To_NSURL(FileSystem_ObjectParentFolder)
		return FileSystem_ObjectParentFolder
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Object_Container>" & space & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Object_Container
on FileSystem_Object_Delete(fileSystemObject)
	try
		set fileSystemObject to my FileSystem_Convert_Object_To_NSURL(fileSystemObject)
		set fileManager to current application's NSFileManager's |defaultManager|()
		(fileManager's trashItemAtURL:fileSystemObject resultingItemURL:(missing value) |error|:(missing value))
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Object_Delete>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Object_Delete
on FileSystem_Object_Kind(fileSystemObject)
	try
		fileSystemObject
		set fileSystemObject to my FileSystem_Convert_Object_To_NSURL(fileSystemObject)
		set {theResult, theUTI, theError} to (fileSystemObject's getResourceValue:(reference) forKey:(current application's NSURLTypeIdentifierKey) |error|:(reference))
		if not theResult then error theError's localizedDescription() as text
		set theUTI to theUTI as text
		set AppleScript's text item delimiters to "."
		set theUTI to text item -1 of theUTI
		set AppleScript's text item delimiters to ""
		return theUTI as text
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Object_Kind>" & space & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Object_Kind
on FileSystem_Object_Name_And_Extension(fileSystemObject)
	try
		set posixPathOfTheFile to FileSystem_Convert_Object_To_NSURL(fileSystemObject)
		set objectName to posixPathOfTheFile's |lastPathComponent|()
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Object_Name>" & space & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Object_Name_And_Extension
on FileSystem_Object_Name(fileSystemObject)
	try
		set fileSystemObject to FileSystem_Convert_Object_To_NSURL(fileSystemObject)
		set FileSystemObjectName to (fileSystemObject's lastPathComponent) as text
		set theExtensionLength to length of ((fileSystemObject's valueForKey:"pathExtension") as text)
		if theExtensionLength > 0 then set FileSystemObjectName to text 1 thru (-1 * (theExtensionLength + 2)) of FileSystemObjectName
		return FileSystemObjectName
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Object_Name>" & space & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Object_Name
on FileSystem_Objects_Names(fileSystemObjectList)
	--https://www.macscripter.net/u/Fredrik71
	--https://macscripter.net/viewtopic.php?id=49262
	try
		set fileSystemObjectList to (fileSystemObjectList) as list
		repeat with currentIndex from 1 to length of fileSystemObjectList
			set item currentIndex of fileSystemObjectList to FileSystem_Object_Name(item currentIndex of fileSystemObjectList)
		end repeat
		return fileSystemObjectList
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<FileSystem_Objects_Names>" & space & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end FileSystem_Objects_Names

on String_Convert_List_To_String(sourceList, textItemDelimiter)
	try
		set the CocoaArray to current application's NSArray's arrayWithArray:sourceList
		set the CocoaString to CocoaArray's componentsJoinedByString:textItemDelimiter
		return (CocoaString as string)
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<String_Convert_List_To_String>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end String_Convert_List_To_String
on String_Text_Items_Of(aString, aDelimiterList)
	try
		set aString to aString as text
		set previousDelimiters to String_TID_Swap(aDelimiterList)
		set theResults to text items of aString
		set AppleScript's text item delimiters to previousDelimiters
		return theResults
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<String_Text_Items_Of>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end String_Text_Items_Of
on String_TID_Swap(theDelimiterList)
	--Usage:Include the following lines or similar in your code to change, store, and then restore the previous TIDS
	--set previousDelimiters to String_TID_Swap(theDelimiterList)
	--<your code using the TIDS>
	--String_TID_Swap(previousDelimiters)
	copy AppleScript's text item delimiters to previousDelimiters
	try
		set AppleScript's text item delimiters to theDelimiterList
		return previousDelimiters
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType
		error "<String_TID_Swap>" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType
	end try
end String_TID_Swap
1 Like

Glad to see you guys have been working on this. Thanks.

On my end, I’m finding that all three of the new scripts (including the latest on 9/10), do NOT find any broken aliases. But, retesting the old one (by wch1zpink on 5/15/21), I find that it still consistently does find the broken aliases.

So far, I’ve only been testing on folders of a few aliases that have one potentially complicating feature: the folders, broken aliases, and original files all live in my locally synched Dropbox folder. I have not tried testing the script on my whole boot drive, which I suppose might find other more “normal” aliases.

I think step one of testing these needs to be the creation of a known positive. Dupe a folder to your desktop, create an alias to it and then delete the folder. Empty the trash (don’t skip this step). Test the script on your desktop folder and see if it reports the known-broken alias.

The fact that these broken aliases are in a Dropbox folder tells me your problem. Dropbox does not support aliases in either the normal or file provider versions of their app. When Dropbox encounters an alias it syncs it as a normal file 0k in size. Nothing of the alias is uploaded. Those aren’t broken aliases at this point, just broken files.

If you want to resolve those aliases you may have to do so manually, or with an alternate version of one of the scripts in this thread modified to just iterate through your once-aliases and search for their original files by name and re-make the aliases. If you want those aliases original files to sync to Dropbox you’ll have to duplicate them to their aliases original Dropbox location.

Yes, your script does work on (freshly broken) aliases to files on my desktop. Conversely, it does not see old broken aliases that I move from my Dropbox folder to the desktop.

FWIW, If I repair a broken “alias” file in Dropbox (using the old script), I can move it back and forth to my desktop and it works either way. Also, I just remembered that the Finder’s “Get Info” window gives an option to “Select New Original” and that works very much like the old script (but apparently by fixing the alias file rather than trashing and replacing it).

Despite whatever voodoo Dropbox might be doing behind the scenes, its handling of alias files is mostly quite robust. For example, I have my Dropbox folder locally synched on multiple computers, when I repair an alias on one, the synched repaired alias points to the original on the other, just like I want.

So, for now, I’m left with two decent ways to repair aliases in my Dropbox folder, one involving wch1zpink’s old script and the other using Finder’s get info, but both operate on one alias at a time. I still would love to get the batch processing efficiency of paulskinner’s latest script, if there was some way to get it to recognize aliases the way wch1zpink’s does.