I expect it’s the ‘whose’ filter choking on the large number of files.
I’ve been working on this today. The version below only uses System Events to get the required data. The script itself checks for visibilty and shell scripts do the subfolder creation and file moving. It works on both my 10.11 and 10.5 systems, handling a 6592-file folder on the latter (a slower machine) in about twenty seconds ” and that’s with BOINC running in the background.
On my 10.11 machine, an identical folder is handled in about five seconds. But it seems to be a good idea here to save the script before running it in Script Editor, otherwise Autosave could cause hanging problems too.
open {choose folder} -- For testing.
-- Droplet 'open' handler: process each item in the drop.
on open droppedItems
repeat with i from 1 to (count droppedItems)
process(item i of droppedItems)
end repeat
end open
-- The main process for each item, in its own handler so that all variables are local and non-persistent.
on process(thisItem)
-- Get a System Events reference to the item and check that it's a folder.
tell application "System Events"
set mainFolder to disk item (thisItem as text)
set isFolder to (mainFolder's class is folder)
end tell
if (isFolder) then
-- Script object having list properties (referenceable for speed of access to items) and the comparison handler for a custom sort.
script o
property fileNames : missing value
property fileCreationDates : missing value
property fileVisibles : missing value
property sortedData : {}
property dateGroupNames : {}
-- Custom-sort comparison handler. Compares two lists from a list of lists ” each containing an integer representing a file's creation date, the file's name, and the value of the file's 'visible' property. The boolean returned to the sort will cause lists representing visible files to be sorted by creation date while those for invisible files are simply shunted to the end.
on isGreater(a, b)
-- List a is "greater" than (ie. should come after) list b if (file b is visible and file a has a later creation date) or (file a is invisible).
return (((end of b) and (beginning of a > beginning of b)) or not (end of a))
end isGreater
end script
-- Get the names, creation dates, and visibles of all the files in the folder.
tell application "System Events"
set {o's fileNames, o's fileCreationDates, o's fileVisibles} to {name, creation date, visible} of files of mainFolder
end tell
-- Collate the data into a list of lists, such that each sublist represents a file and contains an integer derived from its creation date in yyyymmdd format, the quoted form of its name, and its boolean 'visible' value.
repeat with i from 1 to (count o's fileNames)
set {year:yr, month:mn, day:dy} to item i of o's fileCreationDates
set end of o's sortedData to {yr * 10000 + mn * 100 + dy, quoted form of item i of o's fileNames, item i of o's fileVisibles}
end repeat
-- Sort the lists of lists so that those representing visible files are grouped by creation date and any for invisible files are moved to the end.
my CustomShellSort(o's sortedData, 1, -1, {comparer:o})
-- Note the creation-date integer from the first sublist.
set currentFolderDate to beginning of beginning of o's sortedData
-- Get the POSIX path of the folder being processed as we'll be using shell scripts from now on.
set mainFolderPosix to quoted form of POSIX path of thisItem
-- Now work through the list of lists.
repeat with i from 1 to (count o's sortedData)
set {dateNo, fileName, isVisible} to item i of o's sortedData
-- If we hit a list for an invisible file, there are no more visible ones.
if (not isVisible) then exit repeat
-- If we hit a list with a different file creation date, move the files whose names we've collected in connection with the previous date, start a new name list, and set the new creation date as the one being handled.
if (dateNo is not currentFolderDate) then
moveFiles(currentFolderDate, mainFolderPosix, o)
set o's dateGroupNames to {}
set currentFolderDate to dateNo
end if
-- Append the current file name to the list of names associated with the creation date being handled.
set end of o's dateGroupNames to fileName
end repeat
-- When there are no more sublists to be parsed, move the files associated with the last creation date.
moveFiles(currentFolderDate, mainFolderPosix, o)
end if
end process
-- Create a "date" subfolder and move the file(s) with that creation date to it.
on moveFiles(currentFolderDate, mainFolderPosix, o)
-- Derive a "yyyy.mm.dd" text from the yyyymmdd integer and construct a mkdir command to create a subfolder with that name.
tell currentFolderDate as text to set folderDate to text 1 thru 4 & "." & text 5 thru 6 & "." & text 7 thru 8
set subfolderPosix to mainFolderPosix & folderDate
set mkdirCom to "mkdir -p " & subfolderPosix & ";"
set z to (count o's dateGroupNames)
-- We'll be using path expansion in the mv command(s) below to move multiple files, but we can't with single ones.
if (z > 1) then
set b1 to "{"
set b2 to "} "
else
set b1 to ""
set b2 to " "
end if
-- Build and execute mv command(s) to move the files to the subfolder in groups of 1000 or less. The mkdir command creating the subfolder is included in the first shell script.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ","
repeat with i from 1 to z by 1000
set j to i + 999
if (j > z) then set j to z
do shell script (mkdirCom & "mv " & mainFolderPosix & b1 & items i thru j of o's dateGroupNames & b2 & subfolderPosix)
set mkdirCom to ""
end repeat
set AppleScript's text item delimiters to astid
end moveFiles
-- Shell sort. Algorithm: Donald Shell, 1959. AppleScript implementation: Nigel Garvey, 2010.
on CustomShellSort(theList, l, r, customiser)
script o
property comparer : me
property slave : me
property lst : theList
on shsrt(l, r)
set step to (r - l + 1) div 2
repeat while (step > 0)
slave's setStep(step)
repeat with j from (l + step) to r
set v to item j of o's lst
repeat with i from (j - step) to l by -step
tell item i of o's lst
if (comparer's isGreater(it, v)) then
set item (i + step) of o's lst to it
else
set i to i + step
exit repeat
end if
end tell
end repeat
set item i of o's lst to v
slave's rotate(i, j)
end repeat
set step to (step / 2.2) as integer
end repeat
end shsrt
-- Default comparison and slave handlers for an ordinary sort.
on isGreater(a, b)
(a > b)
end isGreater
on rotate(a, b)
end rotate
on setStep(a)
end setStep
end script
-- Process the input parameters.
set listLen to (count theList)
if (listLen > 1) then
-- Negative and/or transposed range indices.
if (l < 0) then set l to listLen + l + 1
if (r < 0) then set r to listLen + r + 1
if (l > r) then set {l, r} to {r, l}
-- Supplied or default customisation scripts.
if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
-- Do the sort.
o's shsrt(l, r)
end if
return -- nothing.
end CustomShellSort