(*
https://macscripter.net/viewtopic.php?id=46207&p=2
Loading file list from a folder
by Shane STANLEY
*)
use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use scripting additions
on listFilesIn:sourceAliasOrFile fileStub:fileStub excludedExtensions:theExtensions
set fileManager to current application's NSFileManager's defaultManager()
set theURLs to fileManager's contentsOfDirectoryAtURL:sourceAliasOrFile includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set thePred to current application's NSPredicate's predicateWithFormat:"%K CONTAINS %@ AND !(%K IN %@)" argumentArray:{"lastPathComponent.stringByDeletingPathExtension", fileStub, "pathExtension", theExtensions}
set theURLs to theURLs's filteredArrayUsingPredicate:thePred
return theURLs as list
end listFilesIn:fileStub:excludedExtensions:
set sourceAliasOrFile to current application's class "NSURL"'s fileURLWithPath:(POSIX path of (path to desktop))
--set sourceAliasOrFile to (path to desktop) # alternate
set fileStub to "First & Last Day"
set theExtensions to {"rtfd"}
my listFilesIn:sourceAliasOrFile fileStub:fileStub excludedExtensions:theExtensions
It returned correctly :
{file “SSD 500:Users:First & Last Day of Last Month by Nigel.applescript", file "SSD 500:Users:First & Last Day of Last Month by Shane.applescript”, file “SSD 500:Users:**********First & Last Day of Last Month by StefanK.applescript”}
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 4 février 2019 18:57:52
Thank you bmose. Interesting way to do it. I’ll try it, because I’d like to see how fast it is.
Thank you, Yvan, for illustrating how to call the objective C routine from AppleScript. I’m amazed at how fast Shane’s code is. I like the AppleScript natural language paradigm, but I’m going to have to learn this language!
I should note that I am using Mojave and I Applescript solutions tend to make one bang into a barrage of requests to allow programs to communicate. It seems like the objective C may be able to do and end-run around this issue by avoiding the calls that trigger it. If I’m wrong about this, please let me know.
This modification to my original post is a little more efficient by extracting file stubs and extensions from the folder’s file names only once rather than twice. If you do try it, it would be interesting to know how it compares with Shane’s ASObjC solution in terms of execution speed. ASObjC is almost always the faster approach, but sometimes plain ol’ AppleScript is sufficiently fast.
on getFilesOfFolder:folderHFSPath withFileStubs:fileStubs excludedExtensions:excludedExtensions
script util
on componentsForFileName:utilFileName
tell (get utilFileName's text items)
if (its length = 1) or ((its length = 2) and (first item = "")) then
set {utilFileStub, utilFileExtension} to {utilFileName, ""}
else
set {utilFileStub, utilFileExtension} to {items 1 thru -2 as text, item -1}
end if
end tell
return {utilFileStub, utilFileExtension}
end componentsForFileName:
end script
set {fileNames, filePOSIXPaths, fileHFSPaths} to {{}, {}, {}}
set excludedExtensions to excludedExtensions as list
tell application "System Events" to set {allNames, allPOSIXPaths, allHFSPaths} to {name, POSIX path, path} of files of folder folderHFSPath
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "."
try
set fileStubsWereComputed to ({fileStubs} is in {{}, "", missing value, null})
if fileStubsWereComputed then
set {allStubs, allExtensions, fileStubs} to {{}, {}, {}}
repeat with currName in allNames
set {currStub, currExtension} to (util's componentsForFileName:(currName's contents))
set {end of allStubs, end of allExtensions} to {currStub, currExtension}
if ({currStub} is not in fileStubs) and ({currExtension} is in excludedExtensions) then set end of fileStubs to currStub
end repeat
else
set fileStubs to fileStubs as list
end if
repeat with i from 1 to allNames's length
set {currName, currPOSIXPath, currHFSPath} to {allNames's item i, allPOSIXPaths's item i, allHFSPaths's item i}
if fileStubsWereComputed then
set {currStub, currExtension} to {allStubs's item i, allExtensions's item i}
else
set {currStub, currExtension} to (util's componentsForFileName:currName)
end if
if ({currStub} is in fileStubs) and ({currExtension} is not in excludedExtensions) then set {end of fileNames, end of filePOSIXPaths, end of fileHFSPaths} to {currName, currPOSIXPath, currHFSPath}
end repeat
set AppleScript's text item delimiters to tid
return {fileNames, filePOSIXPaths, fileHFSPaths}
on error m number n
set AppleScript's text item delimiters to tid
### Error-processing code ###
end try
end getFilesOfFolder:withFileStubs:excludedExtensions:
Is it normal that using %K IN[c] %@ has no effect upon the test upon the extension?
With : set theExtensions to {“RTF”}
the script return:
{file “SSD 500:Users:Barbara studio.rtf", file "SSD 500:Users:BARBARA, Comme Un Soleil Noir.numbers”, file “SSD 500:Users:**********Barbara.rtf”}
With : set theExtensions to {“rtf”} it return :
{file “SSD 500:Users:**********BARBARA, Comme Un Soleil Noir.numbers”}
It’s not frequent to see the extension spelled “RTF” but the problem is common with “pdf” versus “PDF”.
Of course, if there is no need for a list of extensions we may use :
(%K ==[c] %@) and set theExtensions to “RTF” which return
{file “SSD 500:Users:**********BARBARA, Comme Un Soleil Noir.numbers”}
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 6 février 2019 11:42:12
As a practical solution, this variation on Shane’s handler seems to work. I don’t know if it’s supposed to!
use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use scripting additions
on listFilesIn:sourceAliasOrFile fileStub:fileStub excludedExtensions:theExtensions
set fileManager to current application's NSFileManager's defaultManager()
set theURLs to fileManager's contentsOfDirectoryAtURL:sourceAliasOrFile includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set lowercaseExtensions to (current application's NSArray's arrayWithArray:theExtensions)'s valueForKey:"lowercaseString" -- List of lower-cased extensions.
set thePred to current application's NSPredicate's predicateWithFormat:"%K ==[c] %@ AND !(%K.lowercaseString IN %@)" argumentArray:{"lastPathComponent.stringByDeletingPathExtension", fileStub, "pathExtension", lowercaseExtensions} -- Modified predicate.
set theURLs to theURLs's filteredArrayUsingPredicate:thePred
return theURLs as list
end listFilesIn:fileStub:excludedExtensions:
use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use scripting additions
on listFilesIn:sourceAliasOrFile fileStub:fileStub excludedExtensions:theExtensions
set fileManager to current application's NSFileManager's defaultManager()
set theURLs to fileManager's contentsOfDirectoryAtURL:sourceAliasOrFile includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set thePred to current application's NSPredicate's predicateWithFormat:"lastPathComponent.stringByDeletingPathExtension ==[c] %@ AND NOT ANY %@ ==[c] pathExtension" argumentArray:{fileStub, theExtensions}
set theURLs to theURLs's filteredArrayUsingPredicate:thePred
return theURLs as list
end listFilesIn:fileStub:excludedExtensions:
I tried the last routine you posted and it worked well.
I do have one other item to ask about this topic.
When using the list to open files, one can end up switching back-and-forth between default apps. This interleaving of the apps, used to open the files,seems to cause the order on the screen to not match the order in which they were opened. However; if the app calls are grouped, this appears not to be an issue.
Unfortunately; I can’t think of a “fast” way to order the URL list by the app type. It would be great if there was a way to augment your routine to do this. As I’m not that familiar with the AppleScriptObjC world yet, I could use a nudge in the right direction.
My guess was correct. I wrote an AppleScriptObjC sorting routine, for a list of app-filename lists, to make sure that all files with the same app were grouped together. When the list of files was opened, using the default apps, the windows were stacked in the order they had been commanded to open.
It’s a bit slow, but I’ve a lot of debug and logging code in it at present. I hope the lean version will run faster.
The following are the relevant snippets from my code (which I’ll post when I have the move and size windows portion working:
-- --------------------------------------------------------------------------------
-- create a list of the app and the file it should open
-- --------------------------------------------------------------------------------
set theSortedList to {}
repeat with theFile in theList -- this is the output of Shane's routine
-- --------------------------------------------------------------------------------
-- get the name of the default_app for the file that is to be opened.
-- --------------------------------------------------------------------------------
set default_app to getDefaultApp(pathExtensionFromFileReference(theFile))
copy {theFile:(theFile as string), theApp:(default_app as string)} to the end of theSortedList
end repeat
set anArray to current application's NSArray's arrayWithArray:theSortedList
set theDesc to current application's NSSortDescriptor's sortDescriptorWithKey:"theApp" ascending:true selector:"caseInsensitiveCompare:"
set theDesc2 to current application's NSSortDescriptor's sortDescriptorWithKey:"theFile" ascending:true selector:"caseInsensitiveCompare:"
set theSortedList to (anArray's sortedArrayUsingDescriptors:{theDesc, theDesc2}) as list
-------------------------------------------------------
-- code removed here for posting purposes --
-------------------------------------------------------
-- -------------------------------------------------------------
-- handler to get the default app for a given filename extension.
-- -------------------------------------------------------------
on getDefaultApp(pExtStr)
-- Based on @ShaneStanley's script & enhancements
-- http://forum.latenightsw.com/t/how-do-i-get-the-default-app/830/2
-- 2017-11-26
set thePath to (POSIX path of (path to temporary items)) & "temp." & pExtStr
set nsCurApp to current application
--- Create a Temp File with Extension ---
nsCurApp's NSFileManager's defaultManager()'s createFileAtPath:thePath |contents|:(missing value) attributes:(missing value)
set ws to nsCurApp's NSWorkspace's sharedWorkspace()
--- Get URL of Default App for That Extension ---
set nsAppURL to ws's URLForApplicationToOpenURL:(nsCurApp's |NSURL|'s fileURLWithPath:thePath)
--- Get the FileName of the App ---
set {nsResult, nsAppName, nsError} to nsAppURL's getResourceValue:(reference) forKey:(nsCurApp's NSURLLocalizedNameKey) |error|:(reference)
--- Get Just the Root Name of the App ---
set appName to nsAppName as text
set appName to text 1 thru ((offset of "." in (appName as text)) - 1) of appName
return appName
end getDefaultApp
You can potentially speed it up a fair bit by eliminating duplicate calls to the expensive getDefaultApp() handler, something like this:
set theSortedList to {}
set appDetails to current application's NSMutableDictionary's dictionary()
repeat with theFile in theList
-- --------------------------------------------------------------------------------
-- get the name of the default_app for the file that is to be opened.
-- --------------------------------------------------------------------------------
set theExt to pathExtensionFromFileReference(theFile)
set default_app to (appDetails's objectForKey:theExt)
if default_app is missing value then
set default_app to getDefaultApp(theExt)
(appDetails's setObject:default_app forKey:theExt)
end if