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

I am writing a launch daemon that will perform actions on any file in a folder that begins either with a numeral or the “#” character.

This code obviously does not work:

tell application "Finder"
		set beginsList to {"#", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
		set theseItems to every item in thisFolder whose name begins with an item in beginsList 
end tell

I’ve been trying to figure out code that will work, but every experiment has failed. May I beg for help from anyone who can see the answer to this? Thank you.

If my memory is right, neither Finder nor System Events is able to treat this kind of filter.
You may start with :

property allFiles : {}
set thisFolder to path to desktop

set beginsList to {"#", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
set theseItems to {}
tell application "Finder"
	set my allFiles to items in thisFolder
	repeat with aFile in my allFiles
		if first character of (get name of aFile) is in beginsList then set end of theseItems to aFile as alias
	end repeat
end tell
set my allFiles to {}
theseItems

or

property allFiles : {}

set thisFolder to path to desktop

set beginsList to {"#", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
set theseItems to {}
tell application "System Events"
	set my allFiles to disk items in thisFolder
	repeat with aFile in my allFiles
		try
			if first character of (get name of aFile) is in beginsList then set end of theseItems to aFile as alias
		end try
	end repeat
end tell
set my allFiles to {}
theseItems

I inserted try / end try in the second script when I discovered that set maybe to name of aFile failed when aFile is a symlink.

I defined allFiles as a property to fasten the script.

If you aren’t reluctant to use ASObjC you may use :

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions


on listFolder:POSIXPath
	set beginsList to {"#", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
	set theseItems to {}
	set fileManager to current application's NSFileManager's defaultManager()
	set theNames to (fileManager's contentsOfDirectoryAtPath:POSIXPath |error|:(missing value)) as list
	repeat with i from 1 to count of theNames
		if character 1 of (item i of theNames) is in beginsList then
			set end of theseItems to POSIXPath & item i of theNames
		end if
	end repeat
	return theseItems
end listFolder:
its listFolder:(POSIX path of (path to desktop))

It’s seriously faster than the first ones.

I just had the idea to work upon names in vanilla AppleScript. The resulting scripts are really fast so here they are

property allNames : {}
set thisFolder to path to desktop

set beginsList to {"#", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
set theseItems to {}
tell application "Finder"
	set my allNames to name of items in thisFolder
	set thisFolder to thisFolder as text
	repeat with aName in my allNames
		if first character of aName is in beginsList then set end of theseItems to (thisFolder & aName) as alias
	end repeat
end tell
set my allNames to {}
theseItems
property allNames : {}

set thisFolder to path to desktop

set beginsList to {"#", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
set theseItems to {}
tell application "System Events"
	set my allNames to name of disk items in thisFolder
	set thisFolder to thisFolder as text
	repeat with aName in my allNames
		if first character of aName is in beginsList then set end of theseItems to (thisFolder & aName) as alias
	end repeat
end tell
set my allNames to {}
theseItems

Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) vendredi 8 avril 2016 17:50:23

If none of the “files” are bundles, one of these shell scripts may suit you:

set thisFolder to (choose folder)

-- POSIX paths to the files.
set theseFilePaths to paragraphs of (do shell script "find -f " & quoted form of POSIX path of thisFolder & " \\( -name '[#0-9]*' -type f -maxdepth 1 \\)")

-- Or just the names.
set theseFileNames to paragraphs of (do shell script "ls -p " & quoted form of POSIX path of thisFolder & " | egrep '^[#0-9][^/]+$'")

Edit: Inspired by Ivan’s ASObjC script, here’s another version of it. It returns the same results (ie. not diffentiating between folders and files), but does more in the ASObjC realm. It’s thus a bit faster when the folder contains a large number of items.

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

on listFolder:POSIXPath
	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 '[#0-9].+'")
	--return theNames as list
	
	set thePaths to POSIXPath & (theNames's componentsJoinedByString:(linefeed & POSIXPath)) as text
	return thePaths's paragraphs
end listFolder:

its listFolder:(POSIX path of (path to desktop))

Here are two variations on Nigel’s last script. The first is a small change:

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

on listFolder:POSIXPath
	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 '[#0-9].+'")
	--return theNames as list
	set POSIXPath to current application's NSString's stringWithString:POSIXPath
	set thePaths to POSIXPath's stringsByAppendingPaths:theNames
	return thePaths as list
end listFolder:

its listFolder:(POSIX path of (path to desktop))

And the second requires El Capitan, but returns a list of files rather than POSIX paths:

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:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value))
	set theFiles to theFiles's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"lastPathComponent MATCHES '[#0-9].+'")
	return theFiles as list
end listFolder:

its listFolder:(path to desktop)

And I guess we should have at least one example that does differentiate between files, packages and folders:

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].+'")
	-- make mutable array to store files
	set newFiles to current application's NSMutableArray's array()
	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 (newFiles's addObject:aFile)
	end repeat
	return newFiles as list
end listFolder:

its listFolder:(path to desktop)

Edited to fix bug pointed out below.

If you have AppleScript Toolbox installed:

AST list folder thisFolder matching regex "^[#0-9]" with listing subfolders

Hi Shane.

When I run this, the first item in the returned list is always ‘true’. It’s the second item, when coerced to boolean, which indicates whether or not aFile’s a directory. Similarly with the package test. So:

		-- is it a directory?
		set {alwaysTrue, isDirectory} to (aFile's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
		if isDirectory as boolean then
			-- is it a package?
			set {alwaysTrue, 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

The test logic here could of course be reduced to a fairly simple ((not a directory) OR (a package)):

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

on listFolder:aliasOrFile
	set directoryKey to current application's NSURLIsDirectoryKey
	set packageKey to current application's NSURLIsPackageKey

	set fileManager to current application's NSFileManager's defaultManager()
	set theFiles to (fileManager's contentsOfDirectoryAtURL:aliasOrFile includingPropertiesForKeys:{directoryKey, packageKey} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value))
	set theFiles to theFiles's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"lastPathComponent MATCHES '[#0-9].+'")
	-- make mutable array to store files
	set newFiles to current application's NSMutableArray's array()
	repeat with aFile in theFiles
		-- is this item either (not a directory) or (a package)?
		set isFile to ((not (end of (aFile's getResourceValue:(reference) forKey:directoryKey |error|:(missing value))) as boolean) or ¬
			((end of (aFile's getResourceValue:(reference) forKey:packageKey |error|:(missing value))) as boolean))
		if (isFile) then (newFiles's addObject:aFile)
	end repeat
	return newFiles as list
end listFolder:

its listFolder:(path to desktop)

Yep. I’m not sure about your use of simple :), but it’s certainly more efficient.

I’m sorry I’ve taken so long to thank everyone for these extremely helpful replies. I had been reduced to testing for every file whose name begins with 0, adding those to the list, then testing for every file whose name begins with 1, etc.

The answers near the top of the thread all worked beautifully and quickly. I found (as expected) that it was important to save the list as a list of aliases (as the code suggested), otherwise, later bits of my code didn’t work.

Once again, this was a real education in AppleScript (and in logic). Thank you to everyone.

Hello
Except one of them which return filenames, the answers posted return aliases, files (in fact «class furl» items) or POSIX path which are strings.

So even if you need aliases you may easily use them.


# The code of your choice

# Now code using the returned list
set theList to result # I used that because some posted codes don't name the returned list
repeat with aPath in theList
if class of aPath is «class furl» then # the code returned files
	set aPath to aPath as alias
else if class of aPath is text then # the code returned POSIX paths
	set aPath to POSIX file aPath as alias
end if
# Of course, change nothing if the code returned aliases

# here aPath is an alias
# work with this alias
end repeat

Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) samedi 9 avril 2016 18:38:21

Just checking my own understanding of this after referring to Shane’s book and the documentation.

Ignoring the variable names used:
¢ The first item in the returned list is the direct result of the getResourceValue: method: a boolean indicating the success or otherwise of the operation.
¢ The second item is a pointer to where the requested information has been put. This has been set up by the direct parameter value i[/i].
¢ If the |error| parameter value had also been i[/i], there’d be a third item in the list in connection with that.

That’s right. In Objective-C, you’d essentially pass in addresses of a number and an error for the parameters, and the method would populate them. You can’t do that in AppleScript, so the scripting bridge does it for you and passes back a list instead.

I apologize but I don’t understand the behavior of the Shane’s script in message #5.

I ran it twice, from Apple Script Editor and from ASObjC Explorer.
I got :

{file “SSD 500:Users:username:Desktop:0dossier sans titre:”, file “SSD 500:Users:username:Desktop:4dawnam.scpt”, file “SSD 500:Users:username:Desktop:4PeterEvans.numbers”, file “SSD 500:Users:username:Desktop:4see:”, file “SSD 500:Users:username:Desktop:4Shane.scpt”, file “SSD 500:Users:username:Desktop:4Shane2.scpt”, file “SSD 500:Users:username:Desktop:5bug report - copie.rtfd:”}

which is exactly what was returned by the scripts applying the extraneous tests.
As you may see, the first item returned is a folder (dossier sans titre is the french name given to newly created folder) and the last one is a package.
So I wonder what is the extended script role.

Other detail. When I run first Shane’s script on a folder containing symbolic links it returns the symlink :
→ “Users/userName/Desktop/7GIMP 2 - copie.app”
If I run the version requiring El Capitan (or applying POSIX file) it becomes :
→ file “SSD 500:Applications:Applications perso:GIMP 2.app”
which is the file pointed by the link.

Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) dimanche 10 avril 2016 15:58:20

Hi Yvan.

That’s the bug I pointed out in post #7. The script should be testing the second item, not the first, in the list returned by getResourceValue:.

Thanks, Shane.

The real problem in here in is not AppleScript. It’s the RPC-like runtime of the bridge itself like OSAX commands are unable to work with references either. It’s the same limitation for all Objective-C bridges. They only get a copy but are unable to change the contents of the original pointer.

OK
I completely misunderstood the Nigel’s message.
Now it seems clear.
I replaced :
if isDirectory as boolean then
by
if (theValue as boolean) then

and

if not isPackage as boolean then
by
if not (theValue as boolean) then

and now it works well - and I understand what is doing Nigel’s proposal.

Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) dimanche 10 avril 2016 18:21:59

The discriminating ASObjC script(s) can in fact be adapted to build an alias list directly rather than building another array first, coercing it to file list, and then coercing the files:

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

on listFolder:aliasOrFile
	set directoryKey to current application's NSURLIsDirectoryKey
	set packageKey to current application's NSURLIsPackageKey
	
	set fileManager to current application's NSFileManager's defaultManager()
	set theFiles to (fileManager's contentsOfDirectoryAtURL:aliasOrFile includingPropertiesForKeys:{directoryKey, packageKey} 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
		-- is this item either (not a directory) or (a package)?
		set isFile to ((not (end of (afile's getResourceValue:(reference) forKey:directoryKey |error|:(missing value))) as boolean) or ¬
			((end of (afile's getResourceValue:(reference) forKey:packageKey |error|:(missing value))) as boolean))
		if (isFile) then set end of o's aliasList to afile as alias
	end repeat
	
	return o's aliasList
end listFolder:

its listFolder:(path to desktop)

Sorry, I forgot to fix the bug Nigel pointed out. I have now, in case someone looks at it in future.

Nigel,

Your latest script isn’t working for me: Can’t make item 5 of «class ocid» id «data optr00000000804DE70000600000» into type alias.