Add to a list any filename that starts with a number or "#"?

Hello Shane

Thanks for the revised script.

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

Shane and Yvan,

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…

:confused: Interesting indeed. That name doesn’t cause any problems on my machine.

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

Bingo!

Thanks :slight_smile:

Thanks for the feedback.

I didn’t tried in your version but in Nigel’s code, it’s easy to filter this case :

try
	if (isFile) then set end of o's aliasList to aFile as alias
end try

Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) lundi 11 avril 2016 16:21:42

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.

I’m not sure that I use the named method correctly.
In the loop designed to filter the folders, aFile is an URL

(aFile's checkResourceIsReachableAndReturnError:(missing value)) as boolean

returns true for the existing files and also for the broken symLink.

Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) lundi 11 avril 2016 19:17:35

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:

NSFileManager's defaultManager()'s fileExistsAtPath:(aFile's |path|())

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. :wink: 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.

Shane, many thanks for sharing this great script.

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:

  1. IF FILES ONLY: List of Files (POSIX path)
  2. 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

EXAMPLE RESULTS

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.

Nigel,

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.

Hi DJ.

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.

Ah. I see what you’re saying. Fair enough. I’ll change it in posts #31 and #33.

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.

nsurl (lowercase) is a key of the namespace record of SatImage’s XMLLib.osax. That causes the requirement to use pipes.

Somewhat ironic. :slight_smile:

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.