Searching for Alias Files (Recursively – Not Descending into Packages)

Hey Folks,

Does AppleScriptObjC provide a means to search for only Alias files recursively without descending into packages?

If necessary I can find all files and iterate through them checking their class, but I’d rather have something more direct.

TIA.

-Chris

Hi Chris,

You can use Metadata Query. The advantage is that you can pass several folders at a time.

 use AppleScript version "2.5"
use framework "Foundation"
use framework "AppKit"
use scripting additions


set theTargets to {"/Users/me/Desktop/"}

set theQuery to current application's NSMetadataQuery's new()
theQuery's setPredicate:(current application's NSPredicate's predicateWithFormat:"kMDItemContentType == 'com.apple.alias-file' OR kMDItemContentType == 'com.apple.alias-record'")
theQuery's setSearchScopes:theTargets
theQuery's startQuery()
repeat while theQuery's isGathering() as boolean
	delay 0.01
end repeat
theQuery's stopQuery()
set theCount to theQuery's resultCount()
set theResults to current application's NSMutableArray's array()
repeat with iQuery from 0 to (theCount - 1)
	set thePath to ((theQuery's resultAtIndex:iQuery)'s valueForAttribute:(current application's NSMetadataItemPathKey))
	set theURL to (current application's NSURL's fileURLWithPath:thePath)
	(theResults's addObject:theURL)
end repeat
return (theResults's valueForKey:"path") as list -- if you want POSIX paths 
return (theResults) as list -- if ou want HFS files 
1 Like

Hey @ionah,

I’m wanting to avoid Spotlight in this case, but I wasn’t thinking about searching via UTI – so your response get’s me on the right track for this project. (I think…)

Nevertheless – I’ll use the Metadata Query for other things.

Thanks!

-Chris

In this case, you can use:

 use AppleScript version "2.5"
use framework "Foundation"
use framework "AppKit"
use scripting additions

-- get the folder URL
set theFolderURL to current application's NSURL's fileURLWithPath:"/Users/me/Desktop/"

-- get URL of all items in folder, recursively
set keysToRequest to {"NSURLTypeIdentifierKey"}
set theFileManager to current application's NSFileManager's defaultManager()
set allURLs to (theFileManager's enumeratorAtURL:theFolderURL includingPropertiesForKeys:keysToRequest options:6 errorHandler:(missing value))'s allObjects()

-- build the predicate
set thePredicate to (current application's NSPredicate's predicateWithFormat:"NSURLTypeIdentifierKey UTI-CONFORMS-TO 'com.apple.resolvable'") -- This will return all kind of links (including Symbolic links).

-- build an array with URLs satisfying the predicate
set filteredArray to current application's NSMutableArray's new()
repeat with oneURL in allURLs
	set keyValue to (oneURL's resourceValuesForKeys:keysToRequest |error|:(missing value))
	if ((thePredicate's evaluateWithObject:keyValue) as boolean) then (filteredArray's addObject:oneURL) -- This works even if keyValue is missing value.
end repeat

-- convert URL array to file list
return filteredArray as list
1 Like

Hi.

If you’re only interested in alias files, this is more direct and slightly faster — and possibly what you were trying to avoid in the first place :smile: :

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

-- get the folder URL
set theFolderURL to current application's NSURL's fileURLWithPath:"/Users/me/Desktop/"

-- get URL of all items in folder, recursively
set isAliasKey to current application's NSURLIsAliasFileKey
set theFileManager to current application's NSFileManager's defaultManager()
set allURLs to (theFileManager's enumeratorAtURL:theFolderURL includingPropertiesForKeys:{isAliasKey} options:((current application's NSDirectoryEnumerationSkipsPackageDescendants) + ((current application's NSDirectoryEnumerationSkipsHiddenFiles) as integer)) errorHandler:(missing value))'s allObjects()

-- build an array with URLs to alias files
set filteredArray to current application's NSMutableArray's new()
repeat with oneURL in allURLs
	set keyValue to end of (oneURL's getResourceValue:(reference) forKey:isAliasKey |error|:(missing value))
	if (keyValue as boolean) then (filteredArray's addObject:oneURL)
end repeat

-- convert URL array to file list
return filteredArray as list
1 Like

Thanks Nigel.  :smile:

That’s about twice as fast as @ionah’s UTI-based script on a big directory.

I have a need for both methods.

I also have a need to validate aliases.

This is easy enough to do with the Finder – but is there a method using AppleScriptObjC?

-Chris

Hi Chris.

current application's NSURL's URLByResolvingAliasFileAtURL:oneURL options:0 |error|:(missing value)
--> NSURL or missing value

The Xcode documentation doesn’t say what the possible options: are. It only tells of one which isn’t supported. This turns out to be one of three NSURLBookmarkResolutionOptions, the only one which may be of interest here being current application's NSURLBookmarkResolutionWithoutMounting.

1 Like

Hey Nigel,

What’s the best way to make that non-recursive?

This?

set allURLs to (theFileManager's enumeratorAtURL:theFolderURL includingPropertiesForKeys:{isAliasKey} ¬
	options:((current application's NSDirectoryEnumerationSkipsSubdirectoryDescendants) + ¬
	((current application's NSDirectoryEnumerationSkipsHiddenFiles) as integer)) ¬
	errorHandler:(missing value))'s allObjects()

Or something else?

TIA.

-Chris

Hi Chris.

Your quote’s actually from the script @ionah gave you. :slight_smile:

There’s also:

set allURLs to theFileManager's contentsOfDirectoryAtURL:theFolderURL includingPropertiesForKeys:{isAliasKey} ¬
	options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) ¬
	|error|:(missing value)

It’s simpler; but I haven’t compared it with the enumerator method for speed and I imagine the choice of which to use would depend on the overall purpose of the script.

1 Like

Hi guys,

I made a try with Nigel’s script on my Applications folder and found its behavior problematic:

  • the symbolic links are considered as aliases
  • frameworks are not considered as packages

Thus the resulting alias list is not accurate.

Is it a Sierra (10.12.6) bug or do you have the same problems on your systems?

What! Grrrf!

Too many plates in the air…

Thanks.

-Chris

Hey @ionah,

Hmm… I’m running Sierra (10.12.6) as well, and I can confirm the framework issue (at least preliminarily).

I don’t seem to be picking up symlinks, but my test sample is presently quite small.

Thanks.

-Chris

Hmm. :thinking: I can’t comment about symbolic lilnks — except that when I try the script on my own Applications folder, two of the four hits aren’t actually in the heirarchy. Could this be something to do with symbolic links?

What frameworks I’ve found so far on my Mojave machine are ordinary folders in the Finder too. None of them are (visible) in the Applications folder.

I have that problem with at least some frameworks – and all of my Osaxen by Satimage:

/Library/ScriptingAdditions/FITS.osax
/Library/ScriptingAdditions/Files.osax
/Library/ScriptingAdditions/Numerics.osax
/Library/ScriptingAdditions/Satimage.osax
/Library/ScriptingAdditions/XMLLib.osax

Also DJ’s AppleScript Toolbox:

/Users/myUserName/Library/ScriptingAdditions/AppleScript Toolbox.osax

Strange…

-Chris

@NigelGarvey
Mine are mainly Microsoft 2011 frameworks.
They appear in Finder with the “lego brick” icon.
Are they regular folders?

@ccstone
Just for curiosity, why do you want to avoid Spotlight?
Have you encountered bad behaviors with it?

I’m afraid I don’t have any of those.

All of the frameworks in the Frameworks folders of my System, Local, and User Library folders appear with those icons, have disclosure triangles (or whatever they’re called) beside them in Finder list views, and can be double-clicked open like regular folders. There are also a few aliases or links (which may or may not have those icons) to frameworks elsewhere.

Your script and mine return exactly the same results for my Applications folder (including the two hits from outside the hierarchy), my three Frameworks folders, and my three Scripting Additions folders. (Although my third-party OSAXen don’t work in Mojave, they’re still there. None of the Scripting Additions folders produce any hits.)

Just in case the three of us are talking at cross-purposes, I’m referring to ionah’s script and mine, which simply find alias files. I’m not talking about any later adaptation which attempts to follow the alias links.

Frameworks aren’t packages. They’re bundles, and they get their own icon, but they are not considered packages.

From the header file entry for NSURLIsAliasFileKey:

true if the resource is a Finder alias file or a symlink, false otherwise

You can use NSURLFileResourceTypeSymbolicLink to check which.

1 Like

Hi Shane,

Considering that, It’s obvious that Nigel’s script should search in the content of frameworks.
For the same reason, why my script don’t?

Yep.
But to avoid the script searching in unwanted containers, I thought about this:

	set {isAlias, isRegular} to {NSURLIsAliasFileKey, NSURLIsRegularFileKey} of (oneURL's resourceValuesForKeys:{"NSURLIsAliasFileKey", "NSURLIsRegularFileKey"} |error|:(missing value))
	if (isAlias as boolean) and (isRegular as boolean) then (filteredArray's addObject:oneURL)

What’s your opinion?
And what do you think about using Metadata Query for this task?