Looping through file system

How can I write a script that will save a file in the correct folder after finding it’s way to the correct folder via it’s filename?

In other words, I need to loop through numbered folders on a server (example: 10000000-1009999, 1100000-1199999. Then, a second tier of folders are inside these folders, and so on, down to the file level) until the correct location is reached.

So, a file named 1002001 would have to find it’s way down through 4 levels of folders, then save itself.

The following works to get me the name of the second tier of folders, but I CANNOT FIND OUT how to resolve the filepath correctly so the next level loop can find it. I keep getting a “Cannot Get” error. Please help! If you have any scripts to do this type of looping, I’ll gladly change it.


set item_number to 1000000 as text --test file

tell application “Finder” --fills the variable all_folders with the folders of the art archives folder
activate
set file_path to folder “Art Archives” of folder “Desktop” of folder “Klemango” of folder “Users” of startup disk of application “Finder”
set all_folders to every folder of file_path
end tell

my proper_file_name(all_folders) – pulls the first and last names from the network file path to compare to the test number
set {first_name, last_name} to result – sets the first_name and last_name variables to the result of the proper_file_name handler
my second_file_path(item_number, first_name, last_name, all_folders)
set all_folders to result

to proper_file_name(all_folders)
tell application “Finder”
repeat with current_folder in all_folders
set file_Alias to current_folder as alias
set file_Name to name of file_Alias as text
set first_name to first word of file_Name as text
set last_name to second word of file_Name
set folder_reference to {first_name, last_name}
return folder_reference
end repeat
end tell
end proper_file_name

to second_file_path(item_number, first_name, last_name, all_folders)

repeat with current_folder in all_folders
	tell application "Finder"
		
		if (item_number ≥ first_name and item_number ≤ last_name) then
			set file_path to folder (first_name & "-" & last_name) of folder "Art Archives" of folder "Desktop" of folder "Klemango" of folder "Users" of startup disk of application "Finder" as text
			return result
			exit repeat
		end if
		
	end tell
end repeat

end second_file_path

result: “Macintosh HD:Users:Klemango:Desktop:Art Archives:1000000-1099999:”

which is correct, but I can’t go any further than this. The loop should then load the list of folders inside the above range, but I can’t get it to do that. PLEASE HELP ME, I’M MENTAL OVER THIS.

Duh,
set the return value to an alias and now I can access the second tier of folders. Proof that walking away and coming back later is good for you. In this case, it was getting a good night’s sleep.

Thanks Ya’ll!

Hi, Henry.

You haven’t been specific about how the subfolders are named, but I presume that all the folders within your “Art Archives” hierarchy have number-range names in the form “xxxxxxx-xxxxxxx” (where each number has seven digits) and that all the necessary folders already exist.

Rather than plough down through the folder hierarchy on disk a level at a time, it would be easier and faster to get the paths to all the folders in one swoop and then textually pick out the lowest-level one where your file needs to go.

set item_number to "1000000"

-- Get a list of paths to all the folders in the hierarchy.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to linefeed
tell application "Finder" to set folderPaths to text items of ((every folder of entire contents of folder "Art Archives" of desktop) as text)
set AppleScript's text item delimiters to astid

-- 'entire contents' always gets higher-level folders before any subfolders they may contain, so the lowest-level subfolders always come at the end of the list. Search from the end therefore.
repeat with i from (count folderPaths) to 1 by -1
	set thisPath to item i of folderPaths
	-- The last two 'words' of each path are the range-defining numbers (as text).
	set thisRange to words -2 thru -1 of thisPath
	-- Compare each range with the input name and return the path of the first hit. This will be to the lowest-level folder whose range encompasses the input number.
	-- Adjustments will be needed if any of the folders have non-numeric-range names or numerics with other than seven digits.
	if (beginning of thisRange ≤ item_number) and (end of thisRange ≥ item_number) then
		set file_path to thisPath
		exit repeat
	end if
end repeat

file_path

THANK YOU, THANK YOU, THANK YOU.

It will take me awhile to figure out and learn every line of this, but this is fantastic!

I never thought to move from the other end first, but your logic makes a lot of sense to me (now that I’ve been shown the code).

As you can see by my code, I’m just starting out and this is a great learning tool for me. Thank you so much for taking the time to pass your knowledge on so the rest of us (especially me) can benefit. I truly appreciate it!

I’ve read an 800 page book, but nowhere in there does it have this kind of lesson.

Thanks again!

Wow, this is doing great using a test folder with a few folders. But, when I put this to the test at work using a networked server holding thousands of folders, it just poops out and times out.

Surely, there must be a better way? These folder are just being stored as folders on a server, but how do companies keep track of 100’s of thousands, if not millions of records? Would all of these (my) folders somehow have to be saved into a database of some sort to increase sorting/saving speed? Do I have to use the ‘choose’ command to have users manually find their way down the file structure to save?

Thanks again!

Also, I’m using Leopard at home, but work is still stuck with Tiger. Is this my problem?

I hadn’t appreciated there were thousands of folders involved. A shell script (say with the “find” command) would be faster than the Finder in this case. The following is exactly the same script, but using “find” instead of the Finder. The end result is a POSIX path, which you can either use with an “mv” shell script or coerce to a POSIX file (and thence possibly to an HFS path) if you want to use something else:

set item_number to "1000000"

-- Get a list of paths to all the folders in the hierarchy.
set AApath to "Volumes/Disk name/Whatever/Art Archives" -- Substitute the posix path to your folder.
set folderpaths to paragraphs of (do shell script "find -f '" & AApath & "' \\( -type d \\)")

-- "find" always gets higher-level folders before any subfolders they may contain, so subfolders always come after the folders containing them. Search from the end therefore.
repeat with i from (count folderpaths) to 1 by -1
	set thisPath to item i of folderpaths
	-- The last two 'words' of each path are the range-defining numbers (as text).
	set thisRange to words -2 thru -1 of thisPath
	-- Compare each range with the input name and return the path of the first hit. This will be to the lowest-level folder whose range encompasses the input number.
	-- Adjustments will be needed if any of the folders have non-numeric-range names or numerics with other than seven digits.
	if (beginning of thisRange ≤ item_number) and (end of thisRange ≥ item_number) then
		set file_path to thisPath
		exit repeat
	end if
end repeat

file_path

It may be possible to speed things up even more by eliminating some of the possibilities. For instance, this version of the shell script limits the search to paths where name of the top numbered folder and that of the destination folder begin with the first four digits of the file name:

-- Get a list of paths to all the folders in the hierarchy.
set AApath to "Volumes/Disk name/Whatever/Art Archives" -- Substitute the posix path to your folder.
set first4digits to text 1 thru 4 of item_number
set folderpaths to paragraphs of (do shell script "find -f '" & AApath & "' \\( -path '" & AApath & "/" & first4digits & "*' -type d -name '" & first4digits & "*' \\)")

Better still, if your folders have a regular numbering scheme, you could derive the path to the destination folder directly from the file name instead of searching the disk for suitable candidates each time.

Hey, Nigel. Would you please elucidate on that last bit beyond the find? Looking at the man page for find, I see no type or d options and I’m not sure to what that portion equates. Thanks.

Hi, Marc.

It tells “find” to return only directories.

-type is listed under “PRIMARIES” in the “find” manuals on both my Tiger and Snow Leopard machines.

Don’t ask me what most of these mean. :wink:

The manual doesn’t say so, but the escaped parentheses I used in the line appear to be compulsory, otherwise the line throws parameter errors.

WOW,
You guys are blowing me away with this stuff, but I’m gonna learn it! Some of this is way above/beyond me, and I’m trying to learn it as I try to put it into practice.

I don’t know if I’ve asked for more than I needed, but I think I’ve come up with what I need. Just a recursive loop through the file system. This seems to work for me, but now I need to add error checking and other statements. Here is what I’ve come up with:

set item_number to 1253967

tell application “Finder”
set AppleScript’s text item delimiters to “-”
set archive_folders to every folder of folder (alias “ARTDEPT: Art Archives:”)
set folder_count to (count archive_folders)

my find_a_home(archive_folders, folder_count, item_number)

end tell

on find_a_home(archive_folders, folder_count, item_number)

repeat with i from folder_count to 1 by -1
	
	tell application "Finder"
		set file_path to item i of archive_folders as alias
		set archive_name to name of file_path as text
		set first_name to first word of archive_name
		set last_name to last word of archive_name
		
		if (item_number ≥ first_name) and (item_number ≤ last_name) then		
			set sub_folder_ref to every folder of folder file_path
			my find_a_home(sub_folder_ref, (count of sub_folder_ref), item_number)
		end if	
			
	end tell
end repeat

end find_a_home

One thing I need to do and I’m not quite sure where to put the “exists” statement:
I need to check for additional folders in the next matching file-path before continuing to load the next layer of folders into the variable. I guess I do, don’t I? If I get to the bottom of the file tree and there are no “folders”, I’ll know I’m at the bottom and can then save the illustrator file there, in its proper place.

Am I thinking correctly? Again, I want to thank you guys (Nigel!) for taking the time to ponder my problem and offer advice. I save everything you write and slowly put it into practice as I learn. If you ever see my questions in this forum, please reply. Your input is extremely helpful!

Finally got it, for anyone who may still be interested:

tell application “Adobe Illustrator”
tell front document
set item_number to first word of (get name)
end tell
end tell

tell application “Finder”
set AppleScript’s text item delimiters to “-”
set archive_folders to every folder of folder (alias “ARTDEPT: ART ARCHIVES:”)
set folder_count to (count archive_folders)
my find_a_home(archive_folders, folder_count, item_number)
end tell

on find_a_home(archive_folders, folder_count, item_number)

repeat with i from folder_count - 1 to 1 by -1
	tell application "Finder"
		set file_path to item i of archive_folders as alias
		set archive_name to name of file_path as text
		set first_name to first word of archive_name
		set last_name to last word of archive_name
		
		if (item_number ≥ first_name) and (item_number ≤ last_name) then
			set sub_folder_ref to every folder of folder file_path
			set thiscount to count of sub_folder_ref
			if thiscount = 0 then
				my end_script(file_path)
				exit repeat
			end if
			if exists folders then
				my find_a_home(sub_folder_ref, (count of sub_folder_ref), item_number)
			end if
		end if
	end tell
end repeat

end find_a_home

on end_script(filepath)

tell application "Adobe Illustrator"
	activate
	save current document in filepath
end tell

end end_script

Thanks again for all the help from everyone!