Select Next Finder Item

Hi folks.

Is there a way to ‘select next finder item’ without having to build that list first? I’m working with >5800 items and I want to just navigate from item to item down the sorted list by Name. Is this possible?

Example in pseudo-code:

set s to the selected item
loop 10 times
copy selected item to target:folder:alpha
select the next item in that finder window
/loop

It would make copying things over much easier, as the source is a Linux directory where it’s Case-sensitive.

Any insight on this would be appreciated. It seems logical but challenging.

Cheers

This test shows that the items in the folder are returned in unsorted order:


tell application "Finder"
	set sourceFolder to target of Finder window 1
	return items of sourceFolder
end tell

HOWEVER, IF
1) the contents of the folder are already sorted by name in the order A-Z (but not vice versa),
2) you selected one of the folder elements in the window (as the starting point),

and you just need to go to the next element, then your task is solved successfully without re-sorting the contents in the code.

This becomes possible due to the fact that the Finder indexes the contents of the folder exactly in A-Z order. Therefore, you will be able to avoid re-sorting the contents in the code:


set destinationFolder to choose folder

tell application "Finder"
	set sourceFolder to target of Finder window 1
	set theItems to items of sourceFolder
	set aCounter to index of item 1 of (get selection) -- your selected in the folder item index
	repeat 10 times
		duplicate (item aCounter of theItems) to destinationFolder replacing yes
		-- other processing of an item
		set aCounter to aCounter + 1
	end repeat
end tell

Unfortunately, while that’s true (in Mojave anyway), the indexing order is stricter than in a Finder sort-by-name. Case isn’t ignored and numeric strings are treated lexically, not numerically. :frowning: But it takes the Finder quite a while to sort over 5800 items by name, so it may be preferable to use ASObjC methods to get and sort them.

If for any reason daBee still wants the individual items to be selected while they’re being copied, the Finder’s select command will do the job. An alternative might be to move the selection down an item at a time with down-arrow keystrokes, but it might be a fussy job keeping these in sync with the duplicating.

use AppleScript version "2.5" -- El Capitan (10.11) or later
use framework "Foundation"
use scripting additions

main()

on main()
	tell application "Finder"
		activate
		set theFolder to target of front Finder window as alias
		set theSelection to selection
	end tell
	
	set fileManager to current application's class "NSFileManager"'s defaultManager()
	set theURLs to fileManager's contentsOfDirectoryAtURL:(theFolder) includingPropertiesForKeys:({}) options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	set sortDescriptor to current application's class "NSSortDescriptor"'s sortDescriptorWithKey:("path") ascending:(true) selector:("localizedStandardCompare:") -- Specifies an ascending Finder-style sort of the URLs' paths.
	set sortedURLs to theURLs's sortedArrayUsingDescriptors:({sortDescriptor})
	
	if theSelection is {} then
		set aCounter to 1
	else
		set aCounter to (sortedURLs's indexOfObject:(theSelection as alias)) + 1
	end if
	
	script o
		property theItems : sortedURLs as list -- Long list of AS 'file' specifiers.
	end script
	
	repeat with i from aCounter to (count o's theItems)
		set thisItem to item i of o's theItems as alias
		tell application "Finder"
			select thisItem
			
			-- Duplication code here. Or use an ASObjC method (but not in this tell statement).
			
		end tell
	end repeat
end main

Ok, let me reframe what I would like to do, as you’ve touched on something important.

I’m copying over a LAN from a linux box to my workstation. I encounter errors when “Alpha Something” and “alpha something” folder names, which are completely legit on a Linux box, are not on my workstation. Case-sensitivity kills the copy.

rsync is slow and even with a generated report of what isn’t copied, it’s tough to troubleshoot and the mounted NAS device sometimes goes deaf. That’s another forum/thread in itself.

So, I thought AS would be able to essentially step down the list and attempt to copy the directory over, even if it means replacing it. That automation over a day or two would save me a lot of time. Any errors, I could shove in a rescue block that could log to a file for manual attention.

I’d like to have the ability to choose item #300 (sorted by Name) and run the script from there. I don’t need to start fresh again. 299 items have already been covered.

Any ideas?

That timed out, BTW. 5900 directories over a splotchy Gigabit Ethernet.

I draw your attention to this: item 1 of (get selection) in my script is the same 300th item from which you want to start.

But as Nigel Garvey pointed out, Finder indexing doesn’t exactly match sorting by item name. Therefore, my script may give you an inaccurate result.

I don’t know if this is OK for you: you select from 5900 files a certain sequence of files (say, 50 items. Manual, in the window). Then you run this script:


set destinationFolder to choose folder

tell application "Finder"
	set theItems to (get selection)
	repeat with anItem in theItems
		duplicate anItem to destinationFolder replacing yes
	end repeat
end tell

You could try something like this:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions

set sourceFolder to POSIX path of (choose folder with prompt "Choose the remote folder")
set destFolder to POSIX path of (choose folder with prompt "Choose the local folder")
set fileManager to current application's NSFileManager's defaultManager()
set sourceFolder to current application's NSString's stringWithString:sourceFolder
set destFolder to current application's NSString's stringWithString:destFolder
-- get filenames and make lowercase equivalents
set sourceNames to fileManager's contentsOfDirectoryAtPath:sourceFolder |error|:(missing value)
set lowerNames to sourceNames's valueForKey:"lowercaseString"
-- make unique counted set
set dupeSet to current application's NSCountedSet's setWithArray:lowerNames
-- make duplicate of set but uncounted
set temp to current application's NSSet's setWithSet:dupeSet
-- subtract temp from dupeset so only items that were added more than once remain
dupeSet's minusSet:temp
-- use set to build list of actual duplicate names
set dupeArray to dupeSet's allObjects()
set dupes to {}
repeat with aDupe in dupeArray
	set theFilter to (current application's NSPredicate's predicateWithFormat:"SELF ==[c] %@" argumentArray:{aDupe})
	set theMatches to (sourceNames's filteredArrayUsingPredicate:theFilter)
	repeat with aName in theMatches
		set end of dupes to aName as text
	end repeat
end repeat
-- loop through copying
set failures to {} -- holds list of names where errors happen
repeat with i from 1 to count of sourceNames
	if (dupeSet's containsObject:(item i of lowerNames)) as boolean then
		-- try, and ignore error
		(fileManager's copyItemAtPath:(sourceFolder's stringByAppendingPathComponent:(item i of sourceNames)) toPath:(destFolder's stringByAppendingPathComponent:(item i of sourceNames)) |error|:(missing value))
	else
		set {theResult, theError} to (fileManager's copyItemAtPath:(sourceFolder's stringByAppendingPathComponent:(item i of sourceNames)) toPath:(destFolder's stringByAppendingPathComponent:(item i of sourceNames)) |error|:(reference))
		if theResult as boolean is false then
			set end of failures to (item i of sourceNames) as text
		end if
	end if
end repeat
-- show the results
set {saveTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {", "}}
set dupeString to dupes as text
set failureString to failures as text
set AppleScript's text item delimiters to saveTID
display dialog "Copying finished with:" & linefeed & "Failures: " & failureString & linefeed & linefeed & "Duplicates: " & dupeString

Thanks for the post. The job was done manually (many ‘duplicates’). But I will take a look and fidget with Finder selections.

Cheers

I always learn a lot by checking Nigel’s work and so I put his above statement to the test. I created six files in a folder, and the name-sorted Finder window showed the following:

File 1
file 2
File 3
file 4
File 20
file 30

I ran the following script:

tell application "Finder"
	set theFiles to every file in folder theFolder
end tell

And, as Nigel predicted, this returned the files in the following order:

File 1
File 20
File 3
file 2
file 30
file 4

That’s 6 items. Now try 5900. It will time out.

So then it means taking this to Ruby and breaking up the copies per individual directory, coupled with the same recursive challenge of changing the directory name with a suffix, or something like that.

Here I correct a typo in Shane’s proposal and add some instructions to sort as the Finder does.

(*
Beginning of the original code
*)
		if theResult as boolean is false then
			set end of failures to (item i of sourceNames) as text # WAS theFailures
		end if
	end if
end repeat
-- show the results
set {saveTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {", "}}
set dupeString to (my sortAList:dupes) as text
set failureString to (my sortAList:failures) as text
set AppleScript's text item delimiters to saveTID
display dialog "Copying finished with:" & linefeed & "Failures: " & failureString & linefeed & linefeed & "Duplicates: " & dupeString


on sortAList:theList
	set theArray to current application's NSArray's arrayWithArray:theList
	set theArray to theArray's sortedArrayUsingSelector:"localizedStandardCompare:"
	return theArray as list
end sortAList:

With the typo corrected, the original returned:
"Copying finished with:
Failures: File 1, File 11, File 16, File 21, File 26, File 31, File 36, File 41, File 46, File 51, File 56, File 6, File 61, File 66, File 71, File 76, File 81, File 86, File 91, File 96

the edited version return:
“Copying finished with:
Failures: File 1, File 6, File 11, File 16, File 21, File 26, File 31, File 36, File 41, File 46, File 51, File 56, File 61, File 66, File 71, File 76, File 81, File 86, File 91, File 96”

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 25 octobre 2019 18:17:59

Thank you, Yvan. I’ve made the correction above.