I want to move every 10th file from a folder to a new folder. I found this script and tried it, but nothing happens…
This is the script:
set i to 0
set theFolder to (choose folder)
tell application "Finder"
set savedFiles to (make new folder at desktop with properties {name:"Result"})
repeat
try
move item ((i * 3) + 1) of (files of theFolder) to savedFiles
on error
exit repeat
end try
set i to i + 1
end repeat
end tell
“every 10th file from a folder” is a vague concept from the point-of-view of defining which files are actually moved. Is a more-or-less random selection what you want?
When the ‘nth’ file is removed from a folder, the files numbered higher than n are all re-indexed to close the gap. So the file which was the (n + 1)th is now the nth and the file which is now the (2 * n)th was originally the (2 * n + 1)th. So the files get out of sync with the repeat loop. It’s best to get the files as a fixed AppleScript list and work through that rather than through the folder:
set theFolder to (choose folder)
tell application "Finder"
set theFiles to files of theFolder -- AppleScript list of the files.
set savedFiles to (make new folder at desktop with properties {name:"Result"})
repeat with i from 10 to (count theFiles) by 10
move item i of theFiles to savedFiles
end repeat
end tell
I’m not sure that xth file of a folder really make sense.
At least, the script below make what yours was supposed to achieve.
The source folder contained :
doc 1.rtf
doc 2.rtf
doc 3.rtf
doc 4.rtf
doc 5.rtf
doc 6.rtf
doc 7.rtf
doc 8.rtf
doc 9.rtf
doc 10.rtf
doc 11.rtf
doc 12.rtf
displayed in this order according to the way Finder sort names with digits.
set i to 0
set theFolder to (choose folder)
set p2d to path to desktop
tell application "Finder"
set savedFiles to (p2d as text) & "Result:"
if not (exists folder savedFiles) then
set savedFiles to (make new folder at desktop with properties {name:"Result"})
end if
set savedFiles to savedFiles as alias
set theFiles to files of theFolder as alias list
--> {alias "SSD 500:Users:**********:Desktop:source:doc 1.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 10.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 11.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 12.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 2.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 3.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 4.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 5.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 6.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 7.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 8.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 9.rtf"}
(* It means that the app see the list of filenames as:
1 - doc 1.rtf
2 - doc 10.rtf
3 - doc 11.rtf
4 - doc 12.rtf
5 - doc 2.rtf
6 - doc 3.rtf
7 - doc 4.rtf
8 - doc 5.rtf
9 - doc 6.rtf
10 - doc 7.rtf
11 - doc 8.rtf
12 - doc 9.rtf
*)
repeat
try
move item ((i * 3) + 1) of theFiles to savedFiles
on error
exit repeat
end try
set i to i + 1
end repeat
end tell
set nCount to 10
set theFolder to (choose folder) as text
tell application id "com.apple.finder" -- Finder
set savedFiles to (make new folder at desktop with properties {name:"Result"})
set theCount to count of files of folder theFolder
if theCount > nCount - 1 then
set theList to {}
repeat with i from 1 to theCount / nCount
set end of theList to i * nCount
end repeat
move (files of folder theFolder whose index is in theList) to savedFiles
end if
end tell
A couple of other alternatives are reversed indexing …
set theFolder to (choose folder)
tell application "Finder"
set savedFiles to (make new folder at desktop with properties {name:"Result"})
set theCount to (count files of folder theFolder)
repeat with i from (theCount - theCount mod 10) to 10 by -10
move item i of theFolder to savedFiles
end repeat
end tell
… or bulk moving in this manner:
set theFolder to (choose folder)
tell application "Finder"
set theFiles to files of theFolder -- AppleScript list of the files.
set savedFiles to (make new folder at desktop with properties {name:"Result"})
end tell
set filesToMove to {}
repeat with i from 10 to (count theFiles) by 10
set end of filesToMove to item i of theFiles
end repeat
tell application "Finder"
move filesToMove to savedFiles
end tell
Nigel–a quick question that arose in an earlier thread.
Presumably, the source folder contains a lot of files and, if that is the case, would it not be faster to set theFiles as an alias list? I tested your script with the change and it appeared to work.
Thanks for your help.
I now tried to build a loop to make it a little bit faster.
My idea is, to run the script for 5 times. And with each loop a new folder with the number of the loop is created. I tried this, but no luck…
set theFolder to (choose folder)
repeat with a from 1 to 5
tell application "Finder"
set theFiles to files of theFolder -- AppleScript list of the files.
set savedFiles to (make new folder at desktop with properties {name:"Result"& a})
repeat with i from 10 to (count theFiles) by 10
move item i of theFiles to savedFiles
end repeat
a = a + 1
end tell
end repeat
end tell
set theFolder to (choose folder)
repeat with a from 1 to 5
tell application "Finder"
set theFiles to files of theFolder -- AppleScript list of the files.
set savedFiles to (make new folder at desktop with properties {name:"Result" & a})
repeat with i from 10 to (count theFiles) by 10
move item i of theFiles to savedFiles
end repeat
a = a + 1
end tell
end repeat
There’s no need for the a = a + 1 line. a is automatically incremented each time round the outer repeat. In AppleScript, a = a + 1 is just a test to see if a = a + 1, which obviously it doesn’t, so the line generates the unused value false.
Possibly. I was simply churning out suggestions for solving the immediate problem, testing with a folder I happened to have on my desktop. I didn’t look very closely at fine-tuning for speed. But I’d encourage anyone who’s interested to experiment.
I created a test folder containing 5,000 text files and I then ran the script included below ten times, alternating between “as alias list” enabled and then “as alias list” disabled (commented out). The average time of all runs was about 26 seconds and the runs with “as alias list” enabled were consistently one to two seconds slower.
BTW, I used duplicate rather than move to keep the source files intact.
set startTime to (time of (current date))
set theFolder to "Samsung SSD:Test:" as alias
tell application "Finder"
set theFiles to files of theFolder -- as alias list
set savedFiles to (make new folder at desktop with properties {name:"Result"})
repeat with i from 10 to (count theFiles) by 10
duplicate item i of theFiles to savedFiles
end repeat
end tell
set executeTime to (time of (current date)) - startTime
Now I go at work. To make script faster you can try 2 things : 1) use System Events instead of Finder 2) maybe exists command line tool for that task, so try do shell script too
OK. But don’t forget that copying takes longer than moving — and by how much depends largely on the sizes of the items being copied. “Moving” only involves changing entries in the disk catalogue and is therefore faster and more nearly the same whatever’s being “moved”.
Nigel. In retrospect, I didn’t need to move or copy files to see if appending “as alias list” makes things faster or slower. I ran some tests on this and found the latter to be the case. Often an alias list is needed for file processing outside the Finder or for some other reason but otherwise it probably does not need to be used (as was the case with your original script).
It’s pretty much a given in my experience with large lists that an alias list will be faster in comparison to Finder’s own references, which are voluminous. If you have very long lists, you can also prepend a file reference with the word “my” and get more than tenfold performance improvement (isolated to list iteration); e.g.:
[format] move item i of my theFiles to savedFiles
[/format]
Edit: There may be a nominal performance penalty in using these tricks in low iteration settings, and 500 is probably not sufficiently long enough.
Marc Anthony. I’ve included my test script below. It takes 10 seconds to run with a test folder containing 20,000 text files and 20 seconds if I uncomment “as alias list”. What you say makes sense but it doesn’t work that way for me–or perhaps I don’t undertand what is happening here.
Thanks for letting me know about adding the word “my” to the script.
myTest()
on myTest()
set startTime to (time of (current date))
set theFolder to "Samsung SSD:Test:" as alias
tell application "Finder" to set theFiles to (files of theFolder) -- as alias list
set executeTime to (time of (current date)) - startTime
end myTest
BTW, I ran this script from within the Script Editor but I also created separate script applications with and without “as alias list”. The script application without “as alias list” was consistently twice as fast (8 seconds without and 18 seconds with).
FWIW, here’s a pretty fast version using my FileManagerLib script library:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use script "FileManagerLib" version "2.2.1"
use scripting additions
set sourcePath to (choose folder)
set newFolder to create folder at (path to desktop) use name "Result"
set theContents to objects of sourcePath without include folders
set theContents to sort objects theContents sorted property name property sort type Finder like
repeat with i from 10 to count of theContents by 10
move object (item i of theContents) to folder newFolder
end repeat