Efficient Method to Isolate Files?

Hello MacScripters,

I have to isolate 300 audio files from a folder which has 1000 audio files. I have a list of ID numbers which helps identify them.

The script I wrote works, but it is painfully slow. After 12 hours it has only completed 1/3 of what is has to find and move. I’ve read that Finder is notoriously slow, so if anyone is able to suggest tips/tricks I should know about to improve the efficiency of such a task, I’d appreciate it.

set sourceFolder to choose folder
set destinationFolder to choose folder

tell application “Microsoft Excel”
activate
open “/Users/GEC/Desktop/untitled folder/ID Numbers.xlsx”
tell active sheet
select used range
set numList to value of used range
quit
end tell
end tell

with timeout of 86400 seconds – one day
repeat with i from 1 to (count of numList)
set numString to item i of numList as string
tell application “Finder”
move (every file of folder sourceFolder whose name contains numString) to folder destinationFolder
end tell
end repeat
end timeout

Try using “System Events” instead of “Finder”.

Try my example and let me know the timing…

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set sourceFolder to choose folder
set destinationFolder to choose folder

tell application "Microsoft Excel"
	activate
	open "/Users/GEC/Desktop/untitled folder/ID Numbers.xlsx"
	tell active sheet
		select used range
		set numList to value of used range
		quit
	end tell
end tell

with timeout of 86400 seconds -- one day
	set flist to {}
	tell application "System Events"
		repeat with i from 1 to (count of numList)
			set flist to flist & (files in sourceFolder whose name contains (item i of numList as string))
		end repeat
		move every item in flist to destinationFolder
	end tell
end timeout

Thanks so much for your reply! After trying your script I received this error message:

“error “System Events got an error: Can’t make {} into type specifier.” number -1700 from {} to specifier”

Any thoughts?

I can’t test the Merdosoft Excel part but the main part was tested.

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

-- Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 19 juin 2020 22:09:14
----------------------------------------------------------------

property |⌘| : a reference to current application

my Germaine()

on Germaine()
	set sourceFolder to choose folder
	set destinationFolder to choose folder
	
tell application "Microsoft Excel"
    activate
    open "/Users/GEC/Desktop/untitled folder/ID Numbers.xlsx"
    tell active sheet
        select used range
        set numList to value of used range
        quit
    end tell
end tell

	-- set numList to {"metadatas", "cliclick"}
	
	set fileManager to a reference to |⌘|'s NSFileManager's defaultManager()
	
	set skipsSubdirectoryDescendants to |⌘|'s NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer --> 1
	set skipsPackageDescendants to |⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
	set skipsHiddenFiles to |⌘|'s NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
	
	set theOptions to skipsSubdirectoryDescendants + skipsPackageDescendants + skipsHiddenFiles
	
	set NSURLIsDirectoryKey to |⌘|'s NSURLIsDirectoryKey
	set NSURLIsPackageKey to |⌘|'s NSURLIsPackageKey
	set keysToRequest to {NSURLIsDirectoryKey, NSURLIsPackageKey}
	
	set destURL to |⌘|'s NSURL's fileURLWithPath:(POSIX path of destinationFolder)
	
	set theURL to |⌘|'s NSURL's fileURLWithPath:(POSIX path of sourceFolder)
	if (theURL's checkResourceIsReachableAndReturnError:(missing value)) as boolean is false then error "The folder “" & theFolder & "” isn't available."
	
	set allURLs to (fileManager's enumeratorAtURL:theURL includingPropertiesForKeys:keysToRequest options:theOptions errorHandler:(missing value))'s allObjects()
	if allURLs is equal to missing value then error (theError's localizedDescription() as text)
	
	set flatFiles to |⌘|'s NSMutableArray's new()
	repeat with aURL in allURLs
		set {theResult, isDirectory} to (aURL's getResourceValue:(reference) forKey:NSURLIsDirectoryKey |error|:(missing value))
		if isDirectory as boolean then
			-- it's a folder or a package. Skip it
		else -- here are flat files 
			(flatFiles's addObject:aURL) -- fill the array with flatfiles
		end if -- isDirectory as boolean
	end repeat
	
	repeat with aNumString in numList
		set theFormat to "(lastPathComponent CONTAINS[c] '" & aNumString & "')" -- to list  files to move
		set thePredicate to (|⌘|'s NSPredicate's predicateWithFormat:theFormat)
		set someURLs to (flatFiles's filteredArrayUsingPredicate:thePredicate)
		if (count someURLs) > 0 then
			repeat with anURL in someURLs
				set fName to anURL's lastPathComponent()
				-- set movedURL to (destURL's URLByAppendingPathComponent:fName)
				-- move the recognized file
				set {theResult, theError} to (fileManager's moveItemAtURL:anURL toURL:(destURL's URLByAppendingPathComponent:fName) |error|:(reference))
				if theResult as boolean is false then
					if theError's code() as integer ≠ 516 then error (theError's localizedDescription() as string)
				end if
				-- remove the identified file from the main list
				(flatFiles's removeObject:anURL)
			end repeat
		end if
		-- loop to next numString
	end repeat
end Germaine

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 19 juin 2020 22:12:30

I don’t know what you made wrongly but the main part of the script posted by robertfern behave flawlessly.

I ran :

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set sourceFolder to choose folder
set destinationFolder to choose folder
(*
tell application "Microsoft Excel"
	activate
	open "/Users/GEC/Desktop/untitled folder/ID Numbers.xlsx"
	tell active sheet
		select used range
		set numList to value of used range
		quit
	end tell
end tell
*)
set numList to {"metadatas", "cliclick"}
with timeout of 86400 seconds -- one day
	set flist to {}
	tell application "System Events"
		repeat with i from 1 to (count of numList)
			set flist to flist & (files in sourceFolder whose name contains (item i of numList as string))
		end repeat
		move every item in flist to destinationFolder
	end tell
end timeout

and the log history was :

Don't click [Open this Scriplet in your Editor:]
here is a log history
tell application "Script Editor"
	choose folder
		--> alias "SSD 1000:Users:**********:Desktop: sourceFolder:"
	choose folder
		--> alias "SSD 1000:Users:**********:Desktop: destinationFolder:"
end tell
tell application "System Events"
	get every file of alias "SSD 1000:Users:**********:Desktop: sourceFolder:" whose name contains "metadatas"
		--> {file "SSD 1000:Users:**********:Desktop: sourceFolder:get metadatas of a mail.scpt"}
	get every file of alias "SSD 1000:Users:**********:Desktop: sourceFolder:" whose name contains "cliclick"
		--> {file "SSD 1000:Users:**********:Desktop: sourceFolder:with cliclick.applescript"}
	move {file "SSD 1000:Users:**********:Desktop: sourceFolder:get metadatas of a mail.scpt", file "SSD 1000:Users:**********:Desktop: sourceFolder:with cliclick.applescript"} to alias "SSD 1000:Users:**********:Desktop: destinationFolder:"
		--> {file "SSD 1000:Users:**********:Desktop: destinationFolder:get metadatas of a mail.scpt", file "SSD 1000:Users:**********:Desktop: destinationFolder:with cliclick.applescript"}
end tell

May you post the exact script which returned the described error ?
I will try to understand what stroke tomorrow.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 19 juin 2020 22:18:25

Hi Yvan,

Thank you for your reply. I tried running your script. It did not generate an error, which is good, but it also didn’t move any files. It just ran for a few seconds and ended.

Here’s the script you requested that I post. The only thing changed from robertfern’s original script, is the path of the Excel file, which I updated because it was moved to a different folder (I don’t think that should cause a problem).

I should have specified in my original post that I’m running AppleScript 2.7 (ver. 2.11) on MacOs 10.14.5.


use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set sourceFolder to choose folder
set destinationFolder to choose folder

tell application "Microsoft Excel"
	activate
	open "/Users/grantcoughlin/Desktop/Processing/ID Numbers Part 2.xlsx"
	tell active sheet
		select used range
		set numList to value of used range
		quit
	end tell
end tell

with timeout of 86400 seconds -- one day
	set flist to {}
	tell application "System Events"
		repeat with i from 1 to (count of numList)
			set flist to flist & (files in sourceFolder whose name contains (item i of numList as string))
		end repeat
		move every item in flist to destinationFolder
	end tell
end timeout 

Returned: error “System Events got an error: Can’t make {} into type specifier.” number -1700 from {} to specifier.

GEC1227. I don’t have Excel but I wondered if you have checked to see what the numList variable is returning? Also, choose folder returns an alias and these should not be shown as folder and may not work with System Events under Catalina.

Hey peavine, yeah, I checked. The variable returns a 7 digit number as a string, which is what it’s supposed to do. It worked with Finder, but it was just SO slow. I don’t know why it’s not working with System Events.

Thanks for your suggestion though :slight_smile:

The System Events section of robertfern’s script worked for me but only after I made a few Catalina-required changes. As mentioned, I don’t have Excel, so I set numList to some numbers for test purposes only.

set sourceFolder to (choose folder) as text
set destinationFolder to (choose folder) as text

set numList to {111, 222, 333}

with timeout of 86400 seconds -- one day
	set flist to {}
	tell application "System Events"
		repeat with i from 1 to (count of numList)
			set flist to flist & (files in folder sourceFolder whose name contains (item i of numList as string))
		end repeat
		move flist to folder destinationFolder
	end tell
end timeout

Bingo! The changes you mentioned made it work. Thank you SO much, peavine.

Thanks again robertfern and Yvan Koenig :slight_smile:

At this time I don’t know what failed on your machine but on mine, disabling the Excel code and enabling the instruction: set numList to {“metadatas”, “cliclick”}
The files were correctly moved.
Assuming that the culprit was the content of my passed list,
I replaced it by set numList to {111, 222} and added few instructions supposed to report what was done.

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

-- Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 19 juin 2020 22:09:14
----------------------------------------------------------------

property |⌘| : a reference to current application

my Germaine()

on Germaine()
	set sourceFolder to choose folder
	set destinationFolder to choose folder
	(*
tell application "Microsoft Excel"
    activate
    open "/Users/GEC/Desktop/untitled folder/ID Numbers.xlsx"
    tell active sheet
        select used range
        set numList to value of used range
        quit
    end tell
end tell
*)
	set numList to {"metadatas", "cliclick"}
	set numList to {111, 222}
	set fileManager to a reference to |⌘|'s NSFileManager's defaultManager()
	
	set skipsSubdirectoryDescendants to |⌘|'s NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer --> 1
	set skipsPackageDescendants to |⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
	set skipsHiddenFiles to |⌘|'s NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
	
	set theOptions to skipsSubdirectoryDescendants + skipsPackageDescendants + skipsHiddenFiles
	
	set NSURLIsDirectoryKey to |⌘|'s NSURLIsDirectoryKey
	set NSURLIsPackageKey to |⌘|'s NSURLIsPackageKey
	set keysToRequest to {NSURLIsDirectoryKey, NSURLIsPackageKey}
	
	set destURL to |⌘|'s NSURL's fileURLWithPath:(POSIX path of destinationFolder)
	
	set theURL to |⌘|'s NSURL's fileURLWithPath:(POSIX path of sourceFolder)
	if (theURL's checkResourceIsReachableAndReturnError:(missing value)) as boolean is false then error "The folder “" & theFolder & "” isn't available."
	
	set allURLs to (fileManager's enumeratorAtURL:theURL includingPropertiesForKeys:keysToRequest options:theOptions errorHandler:(missing value))'s allObjects()
	if allURLs is equal to missing value then error (theError's localizedDescription() as text)
	log allURLs as list
	set flatFiles to |⌘|'s NSMutableArray's new()
	repeat with aURL in allURLs
		set {theResult, isDirectory} to (aURL's getResourceValue:(reference) forKey:NSURLIsDirectoryKey |error|:(missing value))
		if isDirectory as boolean then
			-- it's a folder or a package. Skip it
		else -- here are flat files 
			(flatFiles's addObject:aURL) -- fill the array with flatfiles
		end if -- isDirectory as boolean
	end repeat
	log flatFiles as list
	repeat with aNumString in numList
		
		set theFormat to "(lastPathComponent CONTAINS[c] '" & aNumString & "')" -- to list  files to move
		set thePredicate to (|⌘|'s NSPredicate's predicateWithFormat:theFormat)
		set someURLs to (flatFiles's filteredArrayUsingPredicate:thePredicate)
		if (count someURLs) > 0 then
			repeat with anURL in someURLs
				set fName to anURL's lastPathComponent()
				-- set movedURL to (destURL's URLByAppendingPathComponent:fName)
				-- move the recognized file
				set {theResult, theError} to (fileManager's moveItemAtURL:anURL toURL:(destURL's URLByAppendingPathComponent:fName) |error|:(reference))
				if theResult as boolean is false then
					if theError's code() as integer ≠ 516 then error (theError's localizedDescription() as string)
				end if
				-- remove the identified file from the main list
				(flatFiles's removeObject:anURL)
			end repeat
		end if
		-- loop to next numString
	end repeat
	
	set movedURLs to (fileManager's enumeratorAtURL:destURL includingPropertiesForKeys:keysToRequest options:theOptions errorHandler:(missing value))'s allObjects()
	log movedURLs as list
end Germaine

#=====
 This is a log history
tell application "Script Editor"
	choose folder
		--> alias "SSD 1000:Users:**********:Desktop: sourceFolder:"
	choose folder
		--> alias "SSD 1000:Users:**********:Desktop: destinationFolder:"
end tell
List of every items in sourceFolder including a folder named Resized_Folder_20200616_121059
(*file SSD 1000:Users:**********:Desktop: sourceFolder:open with BBEdit.scpt, file SSD 1000:Users:**********:Desktop: sourceFolder:Resized_Folder_20200616_121059:, file SSD 1000:Users:**********:Desktop: sourceFolder:with cliclick 222.applescript, file SSD 1000:Users:**********:Desktop: sourceFolder:get metadatas 111 of a mail.scpt*)

List of every flatFiles in sourceFolder (no longer include the folder)
(*file SSD 1000:Users:**********:Desktop: sourceFolder:open with BBEdit.scpt, file SSD 1000:Users:**********:Desktop: sourceFolder:with cliclick 222.applescript, file SSD 1000:Users:**********:Desktop: sourceFolder:get metadatas 111 of a mail.scpt*)
List of files in destinationFolder at the very end
(*file SSD 1000:Users:**********:Desktop: destinationFolder:with cliclick 222.applescript, file SSD 1000:Users:**********:Desktop: destinationFolder:get metadatas 111 of a mail.scpt*)

After writing that, I have an idea: are your Excel files flatFiles like those which I own or are there packages ?

I don’t think that AppleScript version “2.7” makes a difference, it’s the version running on my machine. (ver. 2.11) is the version of Script Editor. It doesn’t matter too.

May you post the complete log history like those which I posted in message #5 and in this one ?

They give more details than a simple: “error “System Events got an error: Can’t make {} into type specifier.” number -1700 from {} to specifier”

With my list of numbers it returned:

Here is a log history

tell application "Script Editor"
	choose folder
		--> alias "SSD 1000:Users:**********:Desktop: sourceFolder:"
	choose folder
		--> alias "SSD 1000:Users:**********:Desktop: destinationFolder:"
end tell
tell application "System Events"
	get every file of alias "SSD 1000:Users:**********:Desktop: sourceFolder:" whose name contains "111"
		--> {file "SSD 1000:Users:**********:Desktop: sourceFolder:get metadatas 111 of a mail.scpt"}
	get every file of alias "SSD 1000:Users:**********:Desktop: sourceFolder:" whose name contains "222"
		--> {file "SSD 1000:Users:**********:Desktop: sourceFolder:with cliclick 222.applescript"}
	move {file "SSD 1000:Users:**********:Desktop: sourceFolder:get metadatas 111 of a mail.scpt", file "SSD 1000:Users:**********:Desktop: sourceFolder:with cliclick 222.applescript"} to alias "SSD 1000:Users:**********:Desktop: destinationFolder:"
		--> {file "SSD 1000:Users:**********:Desktop: destinationFolder:get metadatas 111 of a mail.scpt", file "SSD 1000:Users:**********:Desktop: destinationFolder:with cliclick 222.applescript"}
end tell

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 20 juin 2020 00:55:59

Hi Yvan,

I tried your script and it worked. Perhaps I made a mistake the first time. It was lighting fast too! It found and moved every file in less than 10 seconds–very impressive.

I would post a log of the previous script, as you asked, but I just sent off the files, so I deleted them from my hard drive.

Thanks again for everything. I always learn something from this forum :slight_smile:

Hi.

A bit late to this conversation, but just to mention a few technicalities:

While the original script was reasonable in terms of the AppleScript language itself, the Finder’s actually pretty slow with ‘whose’ filters, especially where the filing system’s concerned. In this case, the script was having to communicate with the Finder — and it with the underlying system — three hundred times in order to have it perform three hundred scans of a folder containing a thousand files (gradually reducing to seven hundred), extract all the files’ names each time, and decide which contained the current ID number.

It’s much faster to tell the Finder (or System Events) just to fetch all the names, once only, and let the script do the thinking. It can then tell the application which files to move instead the app laboriously having to work things out for itself every time.

set numListCount to (count numList)

tell application "Finder" to set fileNames to name of every file of folder sourceFolder

repeat with i from 1 to (count fileNames)
	set thisName to item i of my fileNames
	repeat with j from 1 to numListCount
		if (thisName contains item j of my numList) then
			tell application "Finder" to move file thisName of folder sourceFolder to folder destinationFolder
			-- The next three lines could be uncommented if the numbers were unique to particular names, but it might not make much difference.
			(* set item j of my numList to missing value
			set numList to numList's every text -- (or to numList's every number if that's what the list contains.)
			set numListCount to numListCount - 1 *)
			exit repeat
		end if
	end repeat
end repeat

This is basically what gives Yvan’s script its speed, with his added advantage that there’s no intermediate application (Finder or System Events) between the script and the filing system. The necessary information’s grabbed all in one go and the script itself sifts through the results.

Thank you for the feedback.
I wished to get the log history because I was wondering if there was a problem with the instruction:

set theURL to |⌘|'s NSURL's fileURLWithPath:(POSIX path of sourceFolder)

under Catalina.
As system Events refuse to work with some alias objects, I was assuming that maybe POSIX path was now unable to apply to an alias.
As you wrote that it did its duty, I know that there is no such oddity.s I can’t install Mojave or Catalina, it would be simpler to stop trying to help but I would have to invent a new kind of brain jogging.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 20 juin 2020 11:33:45

That’s interesting Nigel. To test this, I created a folder of 1,000 files, the names of which contained a number ranging from 1 to 1,000. I then tested the time it took to create a list of files to be moved.

The approach I would have used took 1.272 seconds:

set numList to {11, 22, 33, 44, 55, 66, 77, 88, 99}
set sourceFolder to "Save:Test:"
set fileList to {}

tell application "System Events"
	repeat with aNumber in numList
		set fileList to fileList & path of (every file in folder sourceFolder whose name contains (aNumber as text))
	end repeat
end tell

count fileList --> 171

An adaptation of your approach took 0.254 seconds:

set numList to {11, 22, 33, 44, 55, 66, 77, 88, 99}
set sourceFolder to "Save:Test:"
set fileList to {}
set numListCount to (count numList)

tell application "System Events" to set fileNames to name of every file of folder sourceFolder

repeat with i from 1 to (count fileNames)
	set thisName to item i of my fileNames
	repeat with j from 1 to numListCount
		if (thisName contains item j of my numList) then
			set end of fileList to (sourceFolder & thisName)
			exit repeat
		end if
	end repeat
end repeat

count fileList --> 171

So, my revised suggestion to actually move the files is:

set sourceFolder to (choose folder) as text
set destinationFolder to POSIX path of (choose folder)

set numList to {11, 22, 33, 44, 55, 66, 77, 88, 99}

tell application "System Events" to set theFiles to POSIX path of (every file of folder sourceFolder)

set fileList to {}

repeat with i from 1 to (count theFiles)
	set aFile to item i of theFiles
	repeat with j from 1 to (count numList)
		if aFile contains item j of my numList then
			set end of fileList to quoted form of aFile & space
			exit repeat
		end if
	end repeat
end repeat

do shell script "mv " & fileList & space & quoted form of destinationFolder