I ran Nigel last under Script Editor and under ASObjC Explorer.
In both cases I scanned two folders : the desktop and a folder stored on the internal HD although I boot from an external SSD. In the late case the folder contain 305 items, 139 match the filter.
In every cases, the script behaved flawlessly.
If you run it from ASObjC Explorer, I guess that you will see which is exactly the item causing the failure.
Under Apple Script Editor, the events log don’t give many informations
I also edited your own script according to Nigel’s changes and it worked well too.
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
on listFolder:aliasOrFile
set fileManager to current application's NSFileManager's defaultManager()
set theFiles to (fileManager's contentsOfDirectoryAtURL:aliasOrFile includingPropertiesForKeys:{current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value))
set theFiles to theFiles's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"lastPathComponent MATCHES '[#0-9].+'")
-- Initialise an AS list to store the aliases. The script object's only necessary if you're expecting a lot of hits.
script o
property aliasList : {}
end script
repeat with aFile in theFiles
set isFile to true -- flag for file status
-- is it a directory?
set {theResult, isDirectory} to (aFile's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
if isDirectory as boolean then
-- is it a package?
set {theResult, isPackage} to (aFile's getResourceValue:(reference) forKey:(current application's NSURLIsPackageKey) |error|:(missing value))
if not isPackage as boolean then
set isFile to false
end if
end if
if isFile then set end of o's aliasList to aFile as alias
end repeat
return o's aliasList
end listFolder:
its listFolder:(choose folder)
Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) lundi 11 avril 2016 11:58:57
Thanks for the feedback. It’s a relief to know my script doesn’t only work on my own machine!
I’ve eliminated third-party OSAXen as the facilitators of the coercions. If the error were a zero-indexing problem, Shane’s original script would fail too. That just leaves some setting in Shane’s “scripting environment” or something untoward about that particular item in his array. Is it anything other than a file, a folder, or a package? I have no problem with alias files, although since they weren’t specifically considered in the original script, they’re treated as files even when their targets are folders.
I found the problem. I have a file called 36/plus.pdf, which I was using to test some stuff to do with / and : in names. It worked fine with the previous versions, but it seems it won’t coerce to an alias. Interesting…
No more problem here with a file named 36/plus.pdf
Just an idea : maybe the file is a symlink pointing upon a file which no longer exists.
In such case, I get :
error “Impossible de convertir item 2 of «class ocid» id «data optr0000000010EBC07FF37F0000» en type alias.” number -1700 from item 2 of «class ocid» id «data optr0000000010EBC07FF37F0000» to alias
The 1st item is a folder named “0dossier sans titre”
In Nigel script I inserted an instruction :
afile’s lastPathComponent() # ADDED
– is this item either (not a directory) or (a package)?
and ran the script in ASObjC Explorer.
In the events log, the name is reported as : → (NSString) “36:plus.pdf”
Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) lundi 11 avril 2016 15:44:13
Checking if a symbolic link is broken can be done by testing like standard file. When testing if a file exists it should always follow the symbolic link and return the existence of that path. You could use checkResourceIsReachableAndReturnError: method of the NSURL class.
You are right, I was wrong. The problem is that checkResourceIsReachableAndReturnError is not the same as checking if the file exists. It’s an URL validator not checking if there is a file with content there. The correct method would be fileExistsAtPath of NSFileManager:
Here’s a further development for my own amusement which resolves alias files instead of just listing them and lists their targets or not as appropriate. NSURL’s URLByResolvingAliasFileAtURL:options:error: returns the original URL (or perhaps a copy) if applied to anything which isn’t an alias file. In a test with a folder containing 6592 files, none of them alias files, it seems to be slightly faster to apply this method to them anyway than to test their NSURLIsAliasFileKey properties and then not apply it. I don’t know which options to use with it here, so I’ve just used 0.
I’ve also changed the final selection logic from ((not a directory) or (a package)) to ((a regular file) or (a package)). I think this excludes symbolic links from consideration, which doesn’t bother me personally. But it should be easy to include a test for them too if so wished.
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
on listFolder:aliasOrFile
set regularFileKey to current application's NSURLIsRegularFileKey
set aliasFileKey to current application's NSURLIsAliasFileKey
set packageKey to current application's NSURLIsPackageKey
set aliasResolutionOptions to current application's NSNumber's numberWithInteger:(512 + 256)
set fileManager to current application's NSFileManager's defaultManager()
set theNSURLs to (fileManager's contentsOfDirectoryAtURL:aliasOrFile includingPropertiesForKeys:{regularFileKey, aliasFileKey, packageKey} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value))
set theNSURLs to theNSURLs's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"lastPathComponent MATCHES '[#0-9].*'")
-- Initialise an AS list to store the aliases. The script object's not necessary if you're not expecting many hits.
script o
property aliasList : {}
end script
repeat with thisNSURL in theNSURLs
-- If this URL points to an alias file, get the original's URL instead. The process resolves symbolic links too for some reason ” including broken ones!s
set thisNSURL to (current application's |NSURL|'s URLByResolvingAliasFileAtURL:thisNSURL options:aliasResolutionOptions |error|:(missing value))
if (thisNSURL is missing value) then
-- Alias file's original item not found.
else
-- Is this item a regular file?
set {noProblem, isFile} to (thisNSURL's getResourceValue:(reference) forKey:regularFileKey |error|:(missing value))
-- If no problem and the item's either a file or a package, store the URL as an alias.
if ((noProblem) and ((isFile) or (end of (thisNSURL's getResourceValue:(reference) forKey:packageKey |error|:(missing value))))) then set end of o's aliasList to thisNSURL as alias
end if
end repeat
return o's aliasList
end listFolder:
its listFolder:(path to desktop)
Edits: ‘+’ changed to ‘*’ in the lastPathComponent regex in the light of DJ’s objection below. Catch added to skip alias files with missing original items. Variable names changed to match those in post #33 below. “current application’s NSURL’s” changed to “current application’s |NSURL|'s” to avoid an OSAX terminology clash which apparently occurs on some people’s machines. Copes with broken symlinks too.
Since your version which returns only files requires El Capitan, I used your above ASObjC code to create this script, which should run on both Yosemite and El Capitan since I used System Events to filter for files only. I have NOT tested on El Capitan.
I also
¢ Changed the path for the Folder to search to support the ~ shortcut.
¢ Added a parameter for the RegEx pattern
¢ Added a parameter to select search for all Folder items, or just files.
So, this one script will output EITHER:
IF FILES ONLY: List of Files (POSIX path)
OTHERWISE: Compound List of {File (POSIX Path), ItemType, kind} for each Folder Item that matched
Submitted for everyone’s review, comment, and suggestions for improvement.
All feedback very welcome.
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
### EXAMPLE #1 -- FILES ONLY ###
set folderItems to getFolderItems("~/Documents/Test", "[#0-9].+", true)
### EXAMPLE #2 -- ALL ITEMS ###
set folderItems to getFolderItems("~/Documents/Test", "[#0-9].+", false)
--~~~~~~~~~~~~~~~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on getFolderItems(pFolderPath, pMatchesStr, pFilesOnlyBol)
(*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VER: 1.1 2016-04-11
RETURNS:
¢ IF pFilesOnlyBol is true
Simple List of Files (POSIX Path)
¢ OTHERWISE
Compound List of {File (POSIX Path), ItemType, kind} for each Folder Item that matched
where ItemType is "File", "Folder", or "Package"
kind is the Finder "kind" property.
PARAMETERS:
¢ pFolderPath text Folder to search. POSIX path (full or with ~ shortcut)
¢ pMatchesStr text RegEx pattern for file name to match
¢ pFilesOnlyBol boolean Set to true to filter list to files only
AUTHORS:
¢ Shane Stanley
¢ all ASObjC code from his handler listFolder:POSIXPath
¢ http://macscripter.net/viewtopic.php?pid=185545#p185545
¢ JMichaelTX
¢ renamed handler, and added code for files only and item type
¢ Added Handlers:
¢ on getFolderItemType(pItemPath)
¢ on expandPath(pPathStr)
¢ on makeAlias(pAnyPath)
¢ ALL ERRORS & design flaws are mine.
¢ Shane has not had a chance to review or test this script.
REFERENCE:
¢ MacScripter.net: Add to a list any filename that starts with a number or "#"?
http://macscripter.net/viewtopic.php?pid=185540#p185540
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*)
local folderItems
local fileList
local oFldItem
local POSIXPath
local typeList
local itemType
local itemKind
set POSIXPath to expandPath(pFolderPath) ##ADD: JMichaelTX
--- ALL OF THE ASObjC CODE IS FROM SHANE STANLEY ---
-- except for one minor change made by JMichaelTX
-- Replaced hard-coded RegEx pattern with a parameter
set fileManager to current application's NSFileManager's defaultManager()
set theNames to (fileManager's contentsOfDirectoryAtPath:POSIXPath |error|:(missing value))
set theNames to theNames's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:("self MATCHES '" & pMatchesStr & "'")) ##CHG: JMichaelTX
--return theNames as list
set POSIXPath to current application's NSString's stringWithString:POSIXPath
set thePaths to POSIXPath's stringsByAppendingPaths:theNames
--- FILTER THE FOLDER ITEM LIST OR ADD ITEM TYPE --- ##ADD: JMichaelTX
set folderItems to thePaths as list
if (folderItems ≠{}) then
set fileList to {}
repeat with oFldItem in folderItems
set typeList to getFolderItemType(oFldItem)
set itemType to item 1 of typeList
set itemKind to item 2 of typeList
if pFilesOnlyBol and (itemType = "File") then
set end of fileList to oFldItem as text
else if (not pFilesOnlyBol) then
set end of fileList to {oFldItem as text, itemType, itemKind}
end if
end repeat
set folderItems to fileList
end if
return folderItems
end getFolderItems
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on getFolderItemType(pItemPath)
(*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VER: 1.0 2016-04-11
RETURNS: List of {ItemType, kind}
¢ ItemType: "File", "Folder", or "Package"
¢ kind: Finder "kind" property
Examples: "Folder", "rich text (RTF)",
"Portable Network Graphics image"
PARAMETERS:
¢ pItemPath text Folder Item. POSIX path (full or with ~ shortcut)
AUTHOR: JMichaelTX
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*)
local itemType
local pathAlias
set pathAlias to makeAlias(pItemPath)
tell application "System Events"
set oProp to properties of pathAlias
set kindStr to kind of oProp
set pkgBol to package folder of oProp
if pkgBol then
set itemType to "Package"
else if kindStr = "Folder" then
set itemType to "Folder"
else
set itemType to "File"
end if
end tell
--- If you want other Folder Item properties returned, it is easy to add to end of this list ---
return {itemType, kindStr}
end getFolderItemType
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on expandPath(pPathStr)
###BEGIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Name: expandPath(pPathStr)
# Purpose: Converts Path with ~ to Full POSIX Path
# RETURNS: full POSIX path as text
#-------------------------------------------------------------------------------------
# Ver 1.0 2016-04-02
# AUTHOR: JMichaelTX
###””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””
local fullPath
set fullPath to pPathStr
if fullPath = "~" then
set fullPath to (POSIX path of (path to home folder))
else if fullPath starts with "~/" then
set fullPath to (POSIX path of (path to home folder)) & text 3 thru -1 of fullPath
end if
return fullPath
###END~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
end expandPath
on makeAlias(pAnyPath)
## RETURNS: alias to file or folder
## AUTHOR: Adapted by JMichaelTX from Handler by Chris Stone
if class of pAnyPath is alias then
set anyPath to pAnyPath
else if class of pAnyPath is text then
if pAnyPath contains ":" then
set pAnyPath to POSIX path of pAnyPath
end if
set anyPath to alias POSIX file pAnyPath
else if class of pAnyPath is «class furl» then
set anyPath to pAnyPath as alias
end if
return anyPath
end makeAlias
While I’m still enjoying myself, here’s an alternative to my script in post #31. The only significant difference is that it uses one instance of resourceValuesForKeys:error: instead of two of getResourceValue:forKey:error:, allowing the code to be less cluttered. I’ve also changed a couple of variable names better to reflect what they represent.
The post #31 version’s actually about half a second faster on my machine when tested against a folder containing just 6592 suitably named regular files. Presumably this is because it then only ever needs to perform the regular-file checks, whereas the new version always tests if the regular-file and package results together contain ‘true’. However, the new version’s about half a second faster with a folder containing only 6592 folders with qualifying names and still just has the edge with a 3296/3296 mix.
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
on listFolder:aliasOrFile
set regularFileKey to current application's NSURLIsRegularFileKey
set aliasFileKey to current application's NSURLIsAliasFileKey
set packageKey to current application's NSURLIsPackageKey
set aliasResolutionOptions to current application's NSNumber's numberWithInteger:(512 + 256)
set fileManager to current application's NSFileManager's defaultManager()
set theNSURLs to (fileManager's contentsOfDirectoryAtURL:aliasOrFile includingPropertiesForKeys:{regularFileKey, aliasFileKey, packageKey} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value))
set theNSURLs to theNSURLs's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"lastPathComponent MATCHES '[#0-9].*'")
-- Initialise an AS list to store the aliases. The script object's not necessary if you're not expecting many hits.
script o
property aliasList : {}
end script
repeat with thisNSURL in theNSURLs
-- If this URL points to an alias file, get the original's URL instead. The process resolves symbolic links too for some reason ” including broken ones!
set thisNSURL to (current application's |NSURL|'s URLByResolvingAliasFileAtURL:thisNSURL options:aliasResolutionOptions |error|:(missing value))
if (thisNSURL is missing value) then
-- Alias file's original item not found.
else
-- Test filehood and packagehood (!).
set fileOrPackageResults to (thisNSURL's resourceValuesForKeys:{regularFileKey, packageKey} |error|:(missing value))
-- If no problem and one of the results is true, store the URL as an alias.
if ((fileOrPackageResults is not missing value) and (fileOrPackageResults's containsObject:true)) then set end of o's aliasList to thisNSURL as alias
end if
end repeat
return o's aliasList
end listFolder:
set f to (path to desktop)
its listFolder:(f)
Edits: ‘+’ changed to ‘*’ in the lastPathComponent regex in the light of DJ’s objection below. Catch added to skip alias files with missing original items. “current application’s NSURL’s” changed to “current application’s |NSURL|'s” to avoid an OSAX terminology clash which apparently occurs on some people’s machines. Copes with broken symlinks too.
The regex should be [#0-9].* to make a begin with match. A begin-with-match should return true if both expressions are equal (based on emendelson pseudo code).
If anyone is interested: Why didn’t I use it in AST list folder command on the previous page? Because match() ICU function matches the entire string, while AST list folder is not. Therefore regular expressions are differently to get the same results.
The only difference between that and what I used is that your regex would match one-character names, which I didn’t think likely to occur with regular files and packages.
I know, I wasn’t referring to the outcome specifically but only to the regex itself that should perform an begin-with match which it doesn’t. You’re right that In practice the outcome wouldn’t make much difference and it is more than 99.99% accurate but the match is a begin-with followed by at least 1 arbitrary character instead of a begin-witch match.
I’ve made a couple of further edits to my post #31 and post #33 scripts. They now trap for and skip broken alias files and the class name ‘NSURL’ has been enclosed in bars (‘|NSURL|’) to avoid an OSAX terminology clash which apparently afflicts some people’s machines.
At Yvan’s prompting off-forum, and with his help, the scripts now cope with broken symlinks too. For some reason, the URLByResolvingAliasFileAtURL:options:error: method is resolving symlinks as well as alias files. But whereas it returns missing value for a broken alias file, it returns an NSURL for the non-existent target of a broken symlink. So the file/package tests don’t exclude symlinks because they don’t actually get the symlinks. But the error they produce testing non-existent targets is now taken into account and the overall effect is that symlinks are now treated the same as alias files. Their targets are appended to the list if they exist and qualify and are just skipped if they don’t. The name filter is only applied to the alias files or symlinks themselves, not to the targets.