Get Files Whose Displayed Name Contains Even/Odd Numerals

Hi everyone! I’m trying to convert this statement to Applescript but I’m having some trouble.

–Tell application Finder to get all the items in a folder whose displayed file name contains an even numeral (2, 4 ,6 etc… up to 200) and then move those items to another folder

I’d like to do the same thing with files that contain an odd numeral in the displayed file name.

Here is a quick and dirty script doing the job.

I’m not at ease with Regex so I’m sure that it may be a more efficient scheme to achieve the goal but at least it works.

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

on findPattern:thePattern inString:theString
	set theNSString to current application's NSString's stringWithString:theString
	set theOptions to ((current application's NSRegularExpressionDotMatchesLineSeparators) as integer) + ((current application's NSRegularExpressionAnchorsMatchLines) as integer)
	set theRegEx to current application's NSRegularExpression's regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
	set theFinds to theRegEx's matchesInString:theNSString options:0 range:{location:0, |length|:theNSString's |length|()}
	set theFinds to theFinds as list -- so we can loop through
	set theResult to {} -- we will add to this
	repeat with i from 1 to count of items of theFinds
		set theRange to (item i of theFinds)'s range()
		set end of theResult to (theNSString's substringWithRange:theRange) as string
	end repeat
	return theResult
end findPattern:inString:

set sourceContainer to path to desktop as text
set sourceFolder to sourceContainer & "source:"
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair
tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
	# Grab the name of visibles files in the source folder
	set fileNames to name of disk items of folder sourceFolder whose visible is true
	
	repeat with aName in fileNames
		-- log aName
		--set aName to contents of aName
		# Extract the possible number embedded in the file name
		# CAUTION, assumes that there is a single number
		# Thanks to Shane I learnt that in a tell app block we must use « my findPattern », not the standard « its findPattern ».
		set maybe to (my findPattern:"[0-9]" inString:aName) as text
		# Don't move files whose name doesn't embed a number
		if maybe is not "" then
			set maybe to (maybe as number) mod 2
			if maybe = 1 then # move files embedding an odd number
				move disk item (sourceFolder & aName) to folder oddFolder
			else # move files embedding an even number
				move disk item (sourceFolder & aName) to folder evenFolder
			end if
		end if
	end repeat
end tell # System Events

Of course, all of you saw that the used handler is a modified version of a Shane STANLEY’s one.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) jeudi 24 septembre 2015 22:31:16

In my first answer I used an ASObjC handler assuming that it would be faster than good old plain vanilla code.
I was wrong.

When I tested such vanilla code I discovered that it was 5 times faster than the ASObjC one.
So here it is.

on extractNumber(txt)
	set listChars to text items of txt
	set maybe to ""
	repeat with i from 1 to count listChars
		if item i of listChars is in "0123456789" then exit repeat
	end repeat
	repeat with j from i to count listChars
		if item j of listChars is not in "0123456789" then exit repeat
		set maybe to maybe & item j of listChars
	end repeat
	return maybe
end extractNumber


set sourceContainer to path to desktop as text
set sourceFolder to sourceContainer & "source:"
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair

tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
	# Grab the name of visibles files in the source folder
	set fileNames to name of disk items of folder sourceFolder whose visible is true
	
	repeat with aName in fileNames
		# Extract the first possible number embedded in the file name
		set maybe to my extractNumber(aName)
		# Don't move files whose name doesn't embed a number
		if maybe is not "" then
			set maybe to (maybe as number) mod 2
			if maybe = 1 then # move files embedding an odd number
				--move disk item (sourceFolder & aName) to folder oddFolder
			else # move files embedding an even number
				--move disk item (sourceFolder & aName) to folder evenFolder
			end if
		end if
	end repeat
end tell # System Events

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) samedi 26 septembre 2015 16:53:09

Hi. I’m glad you presented a vanilla method, Yvan, as I think ASObjC approaches don’t really serve newbies well.

I tinkered around with a couple variations of this prior to seeing any answers, but there are still outstanding questions that the OP should really clarify, preferably with examples; “up to 200” was specified, but does that mean nothing is possible outside this range or that only things within it should be treated? Additionally, can there be leading zeroes in the text?

Hi.

This uses a shell script with ‘find’ and regex to identify the files and System Events to move them:

set sourceFolder to (choose folder with prompt "Select the source folder.")
set evensFolder to (choose folder with prompt "Select the folder for files with even numbers in their names.")

-- Find files with names containing even numbers ” ie. whose POSIX paths contain an even digit followed by a non-digit which is not "/" and with no further "/"s in the path.
set evens to paragraphs of (do shell script "find -E " & quoted form of POSIX path of sourceFolder & " -maxdepth 1 -regex '.*[02468][^0-9/][^/]*'")

repeat with thisFile in evens
	tell application "System Events" to move file thisFile to evensFolder
end repeat

In my two proposals the number of digits doesn’t matter.

@ Nigel
As I was not sure of what the asker really wanted, I choose to ask System Events to move “even” items in the “even” folder and “odd” items in the “odd” folder.
I thought that it was better than leaving a set in the original folder which would at last contain many files.

Here is an edited version using the same scheme than yours to identify even/odd names.

on extractNumber(txt)
	set listChars to text items of txt
	set maybe to ""
	repeat with i from 1 to count listChars
		if item i of listChars is in "0123456789" then exit repeat
	end repeat
	repeat with j from i to count listChars
		if item j of listChars is not in "0123456789" then
			set lastDigit to item (j - 1) of listChars
			if lastDigit is not in "0123456789" then
				set maybe to ""
				exit repeat
			else
				set maybe to (lastDigit is in "02468")
				exit repeat
			end if
		end if
	end repeat
	return maybe
end extractNumber


set sourceContainer to path to desktop as text
set sourceFolder to sourceContainer & "source:"
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair

tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
	# Grab the name of visibles files in the source folder
	set fileNames to name of disk items of folder sourceFolder whose visible is true
	
	repeat with aName in fileNames
		# Extract the first possible number embedded in the file name
		set maybe to my extractNumber(aName)
		# Don't move files whose name doesn't embed a number
		if maybe is not "" then
			if maybe then # move files embedding an even number
				move disk item (sourceFolder & aName) to folder evenFolder
			else # move files embedding an odd number
				move disk item (sourceFolder & aName) to folder oddFolder
			end if
		end if
	end repeat
end tell # System Events

Its slightly faster than my late one.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) samedi 26 septembre 2015 22:06:04

Hi Yvan.

Oops. Yes, you’re right. I missed the OP’s paragraph about wanting to do the same with odd numbers too. The odd numbers could be found with a similar shell script:

set odds to paragraphs of (do shell script "find -E " & quoted form of POSIX path of sourceFolder & " -maxdepth 1 -regex '.*[13579][^0-9/][^/]*'")

Hello Nigel

I’m not sure that calling twice do Shell Script would be faster than the plain Vanilla.
I compared my different versions with the move instructions disabled so I was able to put the treatment in a repeat 100 times loop allowing me to get meaningful results using Shane’s timer.
I an’t do the same with yours.


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

set beg to current application's NSDate's |date|()
repeat 100 times
	# the code to measure
	delay 0.01
end repeat


set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe

It prove to be fine because when I run it as is, it ends with :
“Done in : 1,106330990791 seconds”
which means : 1 second for the 100 delays + 0,106 second for the execution of the 100 loops.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) samedi 26 septembre 2015 22:21:15

Hello Nigel

My understanding is that your code extract a possible number everywhere in the files paths.
So if the source folder is something like
my:beautiful:128th:folder:

every files will be moved in the even folder.

Am’I right ?

I tested and it appears that I am wrong but I don’t understand why.
If I reduce the shell command to
set evens to paragraphs of (do shell script “find -E " & quoted form of POSIX path of sourceFolder & " -maxdepth 1”)
I get a list of entire pathnames :
the source folder one
the embedded files ones.
What is restricting the search to the filenames ?

I got it.
It’s this subset of your comment : [and with no further "/"s in the path.] which explain why I was wrong.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) samedi 26 septembre 2015 22:28:14

I assumed both even/odd needed to be moved and that some files might either lack numbers or exceed the stated upper limit and should be left in the original folder.


set {isOdd, isEven} to {{}, {}}
set pPathList to do shell script "find ~/Desktop/source/ | egrep '\\d{1,3}'" --isolate files with 1-3 #'s
set integerList to (do shell script "echo " & pPathList & " | tr  -d '[:alpha:]; [:punct:]' ")'s paragraphs  --separate #'s

repeat with index from 1 to count integerList
	tell integerList's item index as number to if it ≥ 1 and it ≤ 200 then
		if (it mod 2) = 1 then
			set isOdd's end to pPathList's paragraph index as POSIX file as alias
		else
			set isEven's end to pPathList's paragraph index as POSIX file as alias
		end if
	end if
end repeat

tell application "Finder"
	if not (exists folder "odd") and not (exists folder "even") then
		make folder with properties {name:"odd"}
		make folder with properties {name:"even"}
	end if
	move isOdd to folder "odd"
	move isEven to folder "even"
end tell

Edited to remove the superfluous awk call.

how fast is this AppleScriptObjC code using Nigel’s regex ? it doesn’t consider the “move” part


use framework "Foundation"
use scripting additions

set sourceFolder to POSIX path of (choose folder)

set fileManager to current application's NSFileManager's defaultManager()
set directoryContents to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
set oddPredicate to current application's NSPredicate's predicateWithFormat:"SELF matches '.*[13579][^0-9/][^/]*'"
set oddResults to directoryContents's filteredArrayUsingPredicate:oddPredicate
set evenPredicate to current application's NSPredicate's predicateWithFormat:"SELF matches '.*[02468][^0-9/][^/]*'"
set evenResults to directoryContents's filteredArrayUsingPredicate:evenPredicate

log oddResults as list
log evenResults as list


Hello Stefan

On the same folder, with a loop repeating the job 100 times, my late one ended with:
“Done in : 0,317521989346 second”
with the same loop, yours ended with :
“Done in : 0,07390999794 second”

What’s long in my vanilla version is the instruction :
set fileNames to name of disk items of folder sourceFolder whose visible is true

The late message of the Original Asker make all our answers incorrect because none of us check that the limit to 200 is respected.
It’s late. I will try to edit my version tomorrow.
I’m unable to edit the shell commands.

In the edited version I will extract the list of filenames with your ASObjC instructions:

set fileManager to current application's NSFileManager's defaultManager()
set directoryContents to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
set fileNames to directoryContents as list

I will use Shane’s timer to compare the old fashioned scheme and the modern one.
I know that some users are reluctant to use such code but it’s time to remove the old dust.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) samedi 26 septembre 2015 23:37:06

For simple things, agreed. But when regular expressions are involved anyway, and the alternatives require some knowledge of several shell commands – including things like awk – it seems to me every bit as approachable, and a good place to start learning it.

Hi Yvan.

I’m not sure the OP does actually want to set a limit. (That would be up to him to clarify.) But assuming there are no leading zeros, these regexes (regices?) seem to do the job:

Even numbers from 2 to 200: ‘.^0-9[^0-9/][^/]
Odd numbers from 1 to 199: ‘.[^0-9]1?[0-9]?[13579][^0-9/][^/]

Hello Nigel

In its late message (2015/09/26 03:53:38 pm), Marc Anthony wrote :

I assumed both even/odd needed to be moved and that some files might either lack numbers or exceed the stated upper limit and should be left in the original folder.

Oops, Marc is a helper, not the original asker.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) dimanche 27 septembre 2015 12:26:19

Here are the announced tests.

(1) version using only vanilla code


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

on extractNumber(txt)
	set listChars to text items of txt
	set maybe to ""
	repeat with i from 1 to count listChars
		if item i of listChars is in "0123456789" then exit repeat
	end repeat
	repeat with j from i to count listChars
		if item j of listChars is not in "0123456789" then exit repeat
		set maybe to maybe & item j of listChars
	end repeat
	return maybe
end extractNumber


set sourceContainer to path to desktop as text
set sourceFolder to sourceContainer & "source:"
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair

tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
	# Grab the name of visibles files in the source folder
	set beg to current application's NSDate's |date|()
	
	repeat 100 times
		set fileNames to name of disk items of folder sourceFolder whose visible is true
		--log fileNames
		repeat with aName in fileNames
			# Extract the first possible number embedded in the file name
			set maybe to my extractNumber(aName)
			# Don't move files whose name doesn't embed a number
			if (maybe is not "") and maybe as number ≤ 200 then
				--log maybe
				set maybe to (maybe as number) mod 2
				if maybe = 1 then # move files embedding an odd number
					--move disk item (sourceFolder & aName) to folder oddFolder
				else # move files embedding an even number
					--move disk item (sourceFolder & aName) to folder evenFolder
				end if
			end if
		end repeat
	end repeat
end tell # System Events

set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe

-->  "Done in : 0,339022994041 second"

(2) version using ASObjC code to read extract the list of files but vanilla code to extract the embedded numbers.


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

on extractNumber(txt)
	set listChars to text items of txt
	set maybe to ""
	repeat with i from 1 to count listChars
		if item i of listChars is in "0123456789" then exit repeat
	end repeat
	repeat with j from i to count listChars
		if item j of listChars is not in "0123456789" then exit repeat
		set maybe to maybe & item j of listChars
	end repeat
	return maybe
end extractNumber


set sourceContainer to path to desktop as text
set sourceFolder to POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair

tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
	# Grab the name of visibles files in the source folder
	
	set beg to current application's NSDate's |date|()
	repeat 100 times
		set fileManager to current application's NSFileManager's defaultManager()
		set directoryContents to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
		set fileNames to directoryContents as list
		--log fileNames
		repeat with aName in fileNames
			# Extract the first possible number embedded in the file name
			set maybe to my extractNumber(aName)
			
			# Don't move files whose name doesn't embed a number
			if (maybe is not "") and maybe as number ≤ 200 then
				--log maybe
				set maybe to (maybe as number) mod 2
				if maybe = 1 then # move files embedding an odd number
					--move disk item (sourceFolder & aName) to folder oddFolder
				else # move files embedding an even number
					--move disk item (sourceFolder & aName) to folder evenFolder
				end if
			end if
		end repeat
	end repeat
end tell # System Events

set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe
-->  "Done in : 0,095301985741 second"

(3) version extracting the list of files with ASObjC code, filtering them with ASObjC code using Regex


use framework "Foundation"
use scripting additions

--set sourceFolder to POSIX path of (choose folder)
set sourceContainer to path to desktop as text
set sourceFolder to POSIX path of (sourceContainer & "source:")


set beg to current application's NSDate's |date|()
repeat 100 times
	set fileManager to current application's NSFileManager's defaultManager()
	set directoryContents to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
	set oddPredicate to current application's NSPredicate's predicateWithFormat:"SELF matches  '.*[^0-9]1?[0-9]?[13579][^0-9/][^/]*'"
	set oddResults to directoryContents's filteredArrayUsingPredicate:oddPredicate
	set evenPredicate to current application's NSPredicate's predicateWithFormat:"SELF matches '.*[^0-9](1?[0-9][02468]|[2468]|200)[^0-9/][^/]*'"
	set evenResults to directoryContents's filteredArrayUsingPredicate:evenPredicate
end repeat


set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe
-->  "Done in : 0,046254992485 second"
log oddResults as list
log evenResults as list


So, we may see that the greater reduction of duration is due to the replacement of vanilla code by ASObjC one to extract the list of files.
At least two complementary tests were possible :
(a) one using list file to extract the filenames but I dropped here because list files is deprecated for years
(b) one using the Finder as an alternative to System Events but as you certainly know, I dislike the Finder which most of the time is slower than System Events.

As you know, I’m really curious so I didn’t resisted to make other tests.
This time the scripts really moved the files which I moved back after each run.

(I) ASObjC code to get the names, vanilla to extract numbers.
I ran it twice.
First with dest folders defined as string, last with the dest folders defined as aliases.
I was surprised to see that using aliases is a slowdowner.


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

on extractNumber(txt)
	set listChars to text items of txt
	set maybe to ""
	repeat with i from 1 to count listChars
		if item i of listChars is in "0123456789" then exit repeat
	end repeat
	repeat with j from i to count listChars
		if item j of listChars is not in "0123456789" then exit repeat
		set maybe to maybe & item j of listChars
	end repeat
	return maybe
end extractNumber


set sourceContainer to path to desktop as text
set sourceFolder to POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair

tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
	
	--set evenFolder to evenFolder as alias # pair
	--set oddFolder to oddFolder as alias # impair
	# Grab the name of visibles files in the source folder
	
	set beg to current application's NSDate's |date|()
	
	set fileManager to current application's NSFileManager's defaultManager()
	set directoryContents to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
	set fileNames to directoryContents as list
	--log fileNames
	repeat with aName in fileNames
		# Extract the first possible number embedded in the file name
		set maybe to my extractNumber(aName)
		
		# Don't move files whose name doesn't embed a number
		if (maybe is not "") and maybe as number ≤ 200 then
			--log maybe
			set maybe to (maybe as number) mod 2
			if maybe = 1 then # move files embedding an odd number
				move disk item (sourceFolder & aName) to folder oddFolder
			else # move files embedding an even number
				move disk item (sourceFolder & aName) to folder evenFolder
			end if
		end if
	end repeat
end tell # System Events

set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe
-->  "Done in : 0,021662950516 second"
-->  "Done in : 0,027228951454 second" if the folder are predefined as aliases

(II) ASObjC to grab the names, ASObjC to apply regex to filter


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

--set sourceFolder to POSIX path of (choose folder)

set sourceContainer to path to desktop as text
set sourceFolder to POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to destContainer & "even:" # pair
set oddFolder to destContainer & "odd:" # impair

tell application "System Events"
	# Create the destination folders if they aren't available
	if not (exists folder evenFolder) then make new folder at end of folder destContainer with properties {name:"even"}
	if not (exists folder oddFolder) then make new folder at end of folder destContainer with properties {name:"odd"}
end tell
set beg to current application's NSDate's |date|()

set fileManager to current application's NSFileManager's defaultManager()
set directoryContents to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
set oddPredicate to current application's NSPredicate's predicateWithFormat:"SELF matches  '.*[^0-9]1?[0-9]?[13579][^0-9/][^/]*'"
set oddResults to directoryContents's filteredArrayUsingPredicate:oddPredicate
set evenPredicate to current application's NSPredicate's predicateWithFormat:"SELF matches '.*[^0-9](1?[0-9][02468]|[2468]|200)[^0-9/][^/]*'"
set evenResults to directoryContents's filteredArrayUsingPredicate:evenPredicate

tell application "System Events"
	repeat with aName in (oddResults as list)
		move file (sourceFolder & aName) to folder oddFolder
	end repeat
	repeat with aName in (evenResults as list)
		move file (sourceFolder & aName) to folder evenFolder
	end repeat
end tell
set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe
-->  "Done in : 0,020846009254 second"

(III) shell script to extract odd and even items, System Events to move them

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
(*
set sourceFolder to (choose folder with prompt "Select the source folder.")
set evensFolder to (choose folder with prompt "Select the folder for files with even numbers in their names.")
*)
set sourceContainer to path to desktop as text
set sourceFolder to POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to (destContainer & "even:") # pair
set oddFolder to (destContainer & "odd:") # impair

set beg to current application's NSDate's |date|()
-- Find files with names containing even numbers ” ie. whose POSIX paths contain an even digit followed by a non-digit which is not "/" and with no further "/"s in the path.
set evens to paragraphs of (do shell script "find -E " & quoted form of POSIX path of sourceFolder & " -maxdepth 1 -regex '.*[^0-9](1?[0-9][02468]|[2468]|200)[^0-9/][^/]*'")

set odds to paragraphs of (do shell script "find -E " & quoted form of POSIX path of sourceFolder & " -maxdepth 1 -regex '.*[^0-9]1?[0-9]?[13579][^0-9/][^/]*'")

tell application "System Events"
	repeat with thisFile in evens
		move file thisFile to folder evenFolder
	end repeat
	repeat with thisFile in odds
		move file thisFile to folder oddFolder
	end repeat
end tell

set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe

--> "Done in : 0,058142006397 second"

(IV) everything with do shell script

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
(*
set sourceFolder to (choose folder with prompt "Select the source folder.")
set evensFolder to (choose folder with prompt "Select the folder for files with even numbers in their names.")
*)
set sourceContainer to path to desktop as text
set sourceFolder to quoted form of POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to quoted form of POSIX path of (destContainer & "even:") # pair
set oddFolder to quoted form of POSIX path of (destContainer & "odd:") # impair

set beg to current application's NSDate's |date|()
-- Find files with names containing even numbers ” ie. whose POSIX paths contain an even digit followed by a non-digit which is not "/" and with no further "/"s in the path.
set evens to paragraphs of (do shell script "find -E " & sourceFolder & " -maxdepth 1 -regex '.*[^0-9](1?[0-9][02468]|[2468]|200)[^0-9/][^/]*'")

set odds to paragraphs of (do shell script "find -E " & sourceFolder & " -maxdepth 1 -regex '.*[^0-9]1?[0-9]?[13579][^0-9/][^/]*'")


repeat with thisFile in evens
	do shell script "mv " & quoted form of thisFile & " " & evenFolder
end repeat
repeat with thisFile in odds
	do shell script "mv " & quoted form of thisFile & " " & oddFolder
end repeat


set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe

--> "Done in : 0,117416977882 second"

The result was so bad that I decided to replace the final loops by a more Unix aware scheme.

(V) This time, the groups of quoted forms of pathnames are moved in a single call.

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
(*
set sourceFolder to (choose folder with prompt "Select the source folder.")
set evensFolder to (choose folder with prompt "Select the folder for files with even numbers in their names.")
*)
set sourceContainer to path to desktop as text
set sourceFolder to quoted form of POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to quoted form of POSIX path of (destContainer & "even:") # pair
set oddFolder to quoted form of POSIX path of (destContainer & "odd:") # impair

set beg to current application's NSDate's |date|()
-- Find files with names containing even numbers ” ie. whose POSIX paths contain an even digit followed by a non-digit which is not "/" and with no further "/"s in the path.
set evens to paragraphs of (do shell script "find -E " & sourceFolder & " -maxdepth 1 -regex '.*[^0-9](1?[0-9][02468]|[2468]|200)[^0-9/][^/]*'")

set odds to paragraphs of (do shell script "find -E " & sourceFolder & " -maxdepth 1 -regex '.*[^0-9]1?[0-9]?[13579][^0-9/][^/]*'")

set evensList to {}
repeat with thisFile in evens
	set end of evensList to quoted form of thisFile
end repeat
set oddsList to {}
repeat with thisFile in odds
	set end of oddsList to quoted form of thisFile
end repeat

do shell script "mv " & my recolle(evensList, " ") & " " & evenFolder
do shell script "mv " & my recolle(oddsList, " ") & " " & oddFolder

set theDiff to (beg's timeIntervalSinceNow()) as real # CAUTION, it's a negative value
set theDiff to -theDiff
if theDiff > 1 then
	set maybe to " seconds"
else
	set maybe to " second"
end if
tell application "SystemUIServer" to display dialog "Done in : " & theDiff & maybe

--> "Done in : 0,065730988979 second"

#=====

on recolle(l, d)
	local oTIDs, t
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
	set t to l as text
	set AppleScript's text item delimiters to oTIDs
	return t
end recolle

#=====

Ouf, that’s all folks.

So, the faster is the scheme using ASObjC to extract the filenames and filter them with regex.
But, as the difference is short, for my own use, if I have to apply a different filter I will try to build a vanilla one because for me, Regex is resembling to ancient Chinese.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) dimanche 27 septembre 2015 16:49:20

Yvan,

A couple of things. It seems to me that, at least in theory, a name could contain two numbers and therefore match both rules, which would cause problems with the regex. Also, the regex is not filtering out invisible files. If you still have the folders set up, perhaps you can time this version too:

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

set sourceContainer to path to desktop as text
set sourceFolder to POSIX path of (sourceContainer & "source:")
set destContainer to path to documents folder as text
set evenFolder to POSIX path of (destContainer & "even:") # pair
set oddFolder to POSIX path of (destContainer & "odd:") # impair
-- make NSStrings of paths
set sourcePath to current application's NSString's stringWithString:sourceFolder
set oddDestPath to current application's NSString's stringWithString:oddFolder
set evenDestPath to current application's NSString's stringWithString:evenFolder

set fileManager to current application's NSFileManager's defaultManager()
-- create folders if they don't exist
fileManager's createDirectoryAtPath:oddDestPath withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
fileManager's createDirectoryAtPath:evenDestPath withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
-- get lists of file names and filter them
set directoryContents to fileManager's contentsOfDirectoryAtPath:sourcePath |error|:(missing value)
set oddPredicate to current application's NSPredicate's predicateWithFormat:"(NOT SELF BEGINSWITH '.') AND (SELF MATCHES '.*[^0-9](1?[0-9][02468]|[2468]|200)[^0-9/][^/]*')"
set oddResults to (directoryContents's filteredArrayUsingPredicate:oddPredicate) -- as list
set evenPredicate to current application's NSPredicate's predicateWithFormat:"(NOT SELF BEGINSWITH '.') AND (SELF MATCHES '.*[^0-9]1?[0-9]?[13579][^0-9/][^/]*')"
set evenResults to (directoryContents's filteredArrayUsingPredicate:evenPredicate)

-- build lists of odd source and destination paths
set oddPaths to sourcePath's stringsByAppendingPaths:oddResults
set oddDests to oddDestPath's stringsByAppendingPaths:oddResults
-- move odd files
repeat with i from 1 to oddPaths's |count|()
	(fileManager's copyItemAtPath:(oddPaths's objectAtIndex:(i - 1)) toPath:(oddDests's objectAtIndex:(i - 1)) |error|:(missing value))
end repeat
-- build lists of even source and destination paths
set evenPaths to sourcePath's stringsByAppendingPaths:evenResults
set evenDests to evenDestPath's stringsByAppendingPaths:evenResults
-- move even files
repeat with i from 1 to evenPaths's |count|()
	(fileManager's copyItemAtPath:(evenPaths's objectAtIndex:(i - 1)) toPath:(evenDests's objectAtIndex:(i - 1)) |error|:(missing value))
end repeat

Hi, Shane. My statement in post #4 could be viewed as hypocritical, given the code I posted. I didn’t intend that to be a starting point for the OP; it was more for experimentation and comparison amongst the regulars, since a workable and more easily understood vanilla method had already been given. Simple is good.

@Nigel:
I spent (an inordinate amount of) time coming up with an alternate even numbers pattern that permits zeros {0, 0x, and 00x}, while limiting the string to 200. It works fine in TextWrangler, but I forgot that neg/pos lookaheads and behinds don’t work in egrep. :frowning: I read somewhere that this might be suitable for a Perl pattern, but I don’t know how to implement that yet. Here it is, for posterity, in all its current uselessness:

((?<!\d)|[01]?(?<![2-9])\d{1})[2468]{1}(?!\d)|((?<!\d)|(?<!\d)\d{1,2})[0]{1,2}(?!\d)

Thanks Shane

(1) my vanilla filter extracts the first number available in a fileName. If they are two, the second one is ignored. I don’t know what to do.

(2) If I understand well, the different codes using regex may be slightly enhanced.
At this time they scan twice the source folder then try to move the files.
My guess is that it would be more efficient to
scan for one family of numbers, move the identified files to the wanted folder
then
scan for the other family of numbers, move the identified files to the wanted folder
Doing that the second scan apply to a smaller number of files so it would be faster.

(3) Of course, all tests were done with the same files from the same source folder and to the same destination folder.
I used a script to move the files back to the source after every attempt.

(4) I tested your proposal.
To be fair, I disabled the two instructions supposed to create the destination folders because they are out of the timed code in other versions.
The timer returned : “Done in : 0,006505012512 second”

BUT there is a drawback, the script doesn’t move the files, it copy them to the destinations but the originals remain in the source folder.

So, I opened Xcode, entered its help and search for copyItemAtPath.
Some lines below the displayed line, I found the function moveItemAtPath.
I replaced copyItemAtPath by moveItemAtPath in the script.
After emptying the destination folders I ran the edited script and got : “Done in : 0,002121984959 second”

More than 10 times faster than faster one of the scripts tested yesterdays.
We got here a new example of the efficiency of ASObjC.

For sure, the used syntax may appear as a bit esoteric but, with the samples which you posted several times here or in the list applescript-users@lists.apple.com, we have a lot of starting points which may be edited to fit specific needs.

On my side I stored numerous ones in a rtf file from which I extract them when needed.
I know that we may store them in Libraries but as you know, most of the scripts I write aren’t for my own use but are answers to askers and in such cases, except when the asker is using Mavericks, leaving the code in the script is easier.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) lundi 28 septembre 2015 10:16:00

I’d be inclined to wait for an “Oh by the way I forgot to mention .” response from the OP before worrying about that. One would need to know which number was relevant in such a case.

I have the opposite problem. SInce I usually use regex with egrep and sed, I keep forgetting how to use lookaheads and behinds and have to look them up every time. :wink: