One way of accessing a file’s metadata attributes is via bash’s mdls command. The challenge with that approach lies in the parsing of the output text for the file’s attribute names and their values.
The current handler simplifies that process by taking advantage of the mdls command’s plist option, which saves the output as a plist file. The plist file is then readily converted to a Cocoa NSDictionary via NSDictionary’s dictionaryWithContentsOfFile: method. Finally, the NSDictionary is coerced to an AppleScript record.
The handler takes as its input argument a reference to the file whose metadata is to be accessed. The reference may be in the form of an HFS path, POSIX path, or AppleScript alias. The handler’s return value is the file’s metadata attributes in the form of an AppleScript record.
An alternative solution is the AST metadata for file command of DJ Bazzie Wazzie’s AppleScript Toolbox plugin. This command combines both execution speed and coding efficiency. It’s only drawback is the need to download and store the OSAX before it can be used.
use framework "Foundation"
use scripting additions
on fileMetadata(fileRef)
try
set posixPath to (fileRef as alias)'s POSIX path
on error
try
set posixPath to (fileRef as POSIX file as alias)'s POSIX path
on error
error "File not found."
end try
end try
set plistPath to do shell script "f=\"/tmp/$(uuidgen).plist\"; mdls " & posixPath's quoted form & " -plist $f; echo $f"
set theMetadata to (current application's NSDictionary's dictionaryWithContentsOfFile:plistPath) as record
current application's NSFileManager's defaultManager()'s removeItemAtPath:plistPath |error|:(missing value)
return theMetadata
end fileMetadata
use scripting additions
use script "Metadata Lib" version "2.0.0"
set theFile to choose file
set theMetadata to fetch metadata for item theFile
But if you’re running 10.11 or later, you can also do it it directly in ASObjC:
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
set theFile to choose file
set mdItem to current application's NSMetadataItem's alloc()'s initWithURL:theFile
set theMetadata to (mdItem's valuesForAttributes:(mdItem's attributes())) as record
Your NSMetadataItem solution returns the same results as the mdls one I presented and is lightningfast. That does appear to be the preferred approach. Very nice!
I incorporated it into my original handler so that it will accept input in the form of an HFS path, POSIX path, or AppleScript alias:
on fileMetadata(fileRef)
try
set fileAlias to fileRef as alias
on error
try
set fileAlias to fileRef as POSIX file as alias
on error
error "File not found."
end try
end try
set mdItem to current application's NSMetadataItem's alloc()'s initWithURL:fileAlias
set theMetadata to (mdItem's valuesForAttributes:(mdItem's attributes())) as record
return theMetadata
end fileMetadata
Can you kindly explain why the initWithURL: method accepts an AppleScript alias argument? I have always created an NSURL object in those situations, but using an AppleScript alias would oftentimes be easier.
Avoiding the overhead of do shell script is nearly always going to be a big win in performance.
Which is what the library version does (including abbreviated POSIX paths). You’re reinventing the wheel
As of macOS 10.11, aliases and file references («class furl») are automatically bridged to NSURLs in this sort of situation. That’s one of the reasons my code includes the AppleScript 2.5 use statement (the other being that 10.11 is also needed for automatic conversion between Cocoa dates and AppleScript dates. The library version handles both conversions pre-10.11, at some potential cost to performance.)
By the way, sadly DJ’s AppleScript Toolbox is no longer:
A minor modification was made to the handler so that it now returns an empty “record” (really an empty list) instead of throwing an error when it encounters the occasional file system item that returns no metadata attributes:
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
on fileMetadata(fileRef)
try
set fileAlias to fileRef as alias
on error
try
set fileAlias to fileRef as POSIX file as alias
on error
error "File not found."
end try
end try
set mdItem to current application's NSMetadataItem's alloc()'s initWithURL:fileAlias
set theMetadata to {}
try
set theMetadata to (mdItem's valuesForAttributes:(mdItem's attributes())) as record
end try
return theMetadata
end fileMetadata
The script included below outputs a selected file’s Spotlight metadata to a text file on the desktop. A few comments:
Just as a matter of personal preference, attribute keys and their values are written on separate lines, but this is easily changed.
The script trims underscores and “kMDItem” from the front of the attribute keys.
Date objects are localized
Some attribute keys have no value, and the values are shown as “not available.”
The timing result with a JPG digital image was 38 milliseconds (not including file write time).
To test this script, open it in a script editor and run.
use framework "Foundation"
use scripting additions
on main()
set theFile to POSIX path of (choose file)
set theFile to current application's |NSURL|'s fileURLWithPath:theFile
set theMetadata to current application's NSMetadataItem's alloc()'s initWithURL:theFile
if theMetadata is missing value then errorAlert("Spotlight metadata not available for the selected file")
set theKeys to theMetadata's attributes()'s sortedArrayUsingSelector:"localizedStandardCompare:"
set theText to ""
set thePattern to "(?i)^_|kMDItem"
set text item delimiters to ", "
repeat with aKey in theKeys
set theValue to (theMetadata's valueForAttribute:aKey)
set theValue to getValue(theValue)
set aKey to (aKey's stringByReplacingOccurrencesOfString:thePattern withString:"" options:1024 range:{0, aKey's |length|()})
set theText to theText & aKey & ":" & linefeed & theValue & linefeed & linefeed
end repeat
set text item delimiters to ""
writeFile(theText)
end main
on getValue(theValue)
try
set isString to (theValue's isKindOfClass:(current application's NSString))
if isString as boolean is true then return theValue as text
set isNumber to (theValue's isKindOfClass:(current application's NSNumber))
if isNumber as boolean is true then return theValue's stringValue() as text
set isDate to (theValue's isKindOfClass:(current application's NSDate))
if isDate as boolean is true then return theValue as date as text
set isArray to (theValue's isKindOfClass:(current application's NSArray))
if isArray as boolean is true then return theValue as list as text
on error
return "Not Available"
end try
end getValue
on writeFile(theText)
set theFile to (current application's NSHomeDirectory()'s stringByAppendingPathComponent:"Desktop")'s stringByAppendingPathComponent:"Spotlight Metadata.txt"
(current application's NSString's stringWithString:theText)'s writeToFile:theFile atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
end writeFile
on errorAlert(dialogMessage)
display alert "An error has occurred" message dialogMessage as critical
error number -128
end errorAlert
main()