Handling Path Lists in Repeat Loops with File Renames

More efficiency learnings for Kevin…

I already have a script that does all this, but I know I’m doing it the tedious way since my script is hugely long and I’m reasonably sure this can be done with list handling, which I’m not yet very good at.

Here’s what the script needs to do:

–user can drag-n-drop any combination of a single file, multiple files, a single folder, multiple folders, and even subfolders. In other words, not be too picky about WHAT is dropped onto it.

–the script ignores the folders and simple should give a list of file paths that it needs to perform operations on in a repeat loop

–the script, based on a flag set by other conditions (think global field) will sometimes need to rename the file, but still keep proper track of it in the list (sometimes the file gets renamed, sometimes not, based on the flag)

–the script should still be able to keep track of that file to perform other tasks (like opening it in Photoshop or a text editor)…so I’m assuming it’s tracking it by a reference somehow.

I’d post my script, but it’s part of a larger script that is under refinement. I’m trying to get some tips/pointers/ideas on list handling, especially when you change the name of a file mid-stream. Right now I cheat…I save the renames for last. Since I never have to deal with that list entry after the rename, it’s a moot point. But this causes me to write other parts of the script rather awkwardly and have extra information tagging along longer than it needs to. I’d like to be able to rename the file yet still be able to blissfully loop through the list of file paths without issue.

The other issue is that the rename should not cause issues with routines running operations in duplicate. For example, if the file is named AAA.pdf and gets renamed ZZZ.pdf, I need to make sure that as the loop progresses that the script doesn’t run on ZZZ.pdf since it’s been renamed.

Hopefully that makes sense…

Kevin, would love to see more of the script here to better make recommendations, but lets throw some intial info out there.

When you loop through a list your looping through it in order of its places not its contents, but I doubt I just made myself clear. If you had

set x to {"B", "C", "E", "F"}

you are looping through by reference to the item not to the items content… So item 2 is C… if you later change item 2 to “A” it is still item 2 not item 1 even though “A” comes before “B”. So unless you are sorting your list I don’t quite see the problem… Again though some more info my bring something to light I’m not considering.

Why not do the renames first then reassemble a new list?

Or does the rename need to happen mid loop?

-N

While we wait to hear back from you though here is one way to parse through any items dropped for any type of files and build a master list of JUST the files.

property MasterList : {}

on adding folder items to thisFolder after receiving theseItems
	set MasterList to {}
	repeat with thisItem in theseItems
		buildMasterList(thisItem)
	end repeat
	display dialog (MasterList as string)
end adding folder items to

on buildMasterList(thisItem)
	if (folder of (info for thisItem)) then
		tell application "Finder" to set subItems to items of folder thisItem
		repeat with anItem in subItems
			buildMasterList(anItem as alias)
		end repeat
	else
		set end of MasterList to {thisItem as string, false}
	end if
end buildMasterList

A few things to notice…

I’m declaring the master list as a property so there is no need to worry about the scope of your main variable (list). Because properties retain values though I am resetting it at run time.

I am also adding a bool to each file so master list actually ends up looking like this

MasterList {{“path:to:file1”, false}, {“path:to:file2”, false}… etc…}

so that way if your worried about tracking whats been handled and what hasn’t you can check for it… and when you DO handle a file change it so for example you can easily during some routine check with the following.

repeat with anItem in MasterList
	if (item 2 of anItem) then
		-- item 2 had been changed to true elsewhere and I've already been handled
		-- do whatever is appropriate
	else
		-- item 2 is still false and I have not been handled
		-- do whatever is appropriate
	end if
end repeat

So hope that helps. I’m still not entirely sure what you’re looking for though so if we could see more of the script it would help.

The naming is most conveniently done mid-loop based on checks that determine other changes as well. The file is being “examined” by a multitide of handlers. One fixes it’s extension, one does file type and creator, another determines if the icon preview is missing and needs regenerating, etc. The extension “fixer” is the one that renames the file (adding, subtracting, or changing it to an appropriate extension).

What I want to happen is once a certain check/fix passes, it spawns other related checks/fixes. Right now, some checks/fixes are happening more than once because they have to be freshly re-spawned, separate from the previous spawn. Very inefficient. (Imagine checking to see if a file is a PDF in case in needs file/creator type fixed…then having to re-check if it is a PDF to see if it needs renaming.)

Wish I could post my exeisting script, but it’s over 1000 lines long to get the full sweep of it…and I’m in the middle of re-writing it. It’s my project that restores type/creator/extension information on graphics files that have lost their resource forks and sometimes have had improper extensions applied to them. Never done anything with so many “if” clauses…LOL. Check the check of the check’s check before checking the other check’s check. ;p

James’ information might be useful…if I have the Finder rename the file and rename it in-place in the list of paths, it sounds like that will handle things okay.

So how about a simple sub-question…in an “on open” handler (droplet)…how do I reduce the list the “on open” handler gathers to just a list of file paths, without being cluttered by folders? My parsing only deals with files, not folders.

How are you renaming files? Also, what kind of repeat are you using?

-- One
repeat with i from 1 to whatever
end

-- Two
repeat with thisItem in theseItems
end

I tried renaming with the Finder (using the second repeat loop, if it matters), and I didn’t have to make any changes to the list to account for the new name.

Edit:

I believe you have to handle the clutter yourself.

FWIW, I was testing this on a single file (named “untitled.txt”):

on run
	choose file with multiple selections allowed without invisibles
	open result
end run

on open theseItems
	repeat with thisItem in theseItems
		set {name:thisName, folder:isFolder} to (info for thisItem)
		
		if not isFolder then
			set shouldRename to false
			if thisName starts with "untitled" then set shouldRename to true
			
			if shouldRename then
				tell application "Finder"
					set name of thisItem to ("Renamed " & thisName)
					return name of thisItem
					--> should return the new name
				end tell
			end if

			-- You could also check the list item here
			-- return thisItem
		end if
	end repeat
end open

See my above post, that came in no doubt as you were writing this.

I would start by flowcharting this… come up with a good flow chart then work from there. My guess is your code is tad bloated ( you also told us :D) and by taking the code away and just thinking about the process overview you could really streamline your code.

Maybe because it’s late in the day, I better rephrase, since I need to split the two tasks apart:

–How do I, in an “on open” handler, reduce the list AppleScript returns from the “drop” onto the droplet to just a list of files. No renaming, just a cleanup of the list, ditching all the folder references.

–Separately, how would I take any given item in the list and: a) rename the file at the finder, and b) also rename it in-place in the list (so the list accurately reflects the current state of the files). Or, as one of you may have alluded to…do I need to rename the reference in the list or is it simply a “pointer” of sorts and automatically reflects the “new” file name?

Sorry, if I knew this was going to be so complicated I would have broken this up better. I really need two SEPARATE handlers.

As for the flowcharting, I have a good grasp of what I want it to do, it’s the specifics of doing it that I know I’m taking the long way around at. :wink:

Here is my “folder cleaner upper”…

on folderFinder(alias_to_check)
	if (alias_to_check as string) ends with ":" then
		return "yes"
	else
		return "no"
	end if
end folderFinder

        --this is a small chunk of the "on open" handler
	repeat with i from 1 to count of items of actionItems
		set current_path to item i of actionItems
		
		if folderFinder(current_path) is "yes" then
			my logMe("¢ Pulling subfolder information...", 0)
			tell application "Finder"
				try
					set subfolder_list to files of current_path's entire contents as alias list
				on error
					set subfolder_list to {first file of current_path's entire contents as alias}
				end try
			end tell
			
			set actionItems to actionItems & subfolder_list
		end if
	end repeat

Kevin two things,

1: Should the droplet search through folders dropped and add the FILES to the list?

2: If you remove folder references how are you going to refer to the file later to actually rename it etc?

I’m going to make a few assumptions here though and say that no you don’t want to search through folders for files, but yes you want to retain file paths with the file name.

So here is the Open handler

on open fileList
	set MasterList to {}
	repeat with aFile in fileList
		if not (folder of (info for aFile)) then tell application "Finder" to set end of MasterList to {name of aFile, container of aFile as string}
	end repeat
end open

Now how to rename a file and its referrence, lets say for a second you want to add “1_” before each of the files, well then you could do this.

on open fileList
	set MasterList to {}
	repeat with aFile in fileList
		if not (folder of (info for aFile)) then tell application "Finder" to set end of MasterList to {name of aFile, container of aFile as string}
	end repeat
	tell application "Finder"
		repeat with aFile in MasterList
			set newName to "1_" & (item 1 of aFile)
			set {name of ((item 2 of aFile & item 1 of aFile) as alias), item 1 of aFile} to {newName, newName}
		end repeat
	end tell
	display dialog MasterList as string
end open

Hope some of that helps, and if not let me know where I’m misunderstanding :smiley:

Any folder(s) dropped on the droplet, or any mix of files and folders, should search through any sub-folders. I don’t need the folder references themselves since as near as I can tell the list is composed of a complete path. I just don’t need the paths that reference a folder.

Or another way, I need a complete drill-down list of complete paths from the dropped content, but without the paths that are “just a folder”…I only care about the paths that reference a file of some sort.

That help?

(I’m at home right now so can’t try any of this until tomorrow…)

Hey Kevin, Okay I think I follow you this time… actually I think I followed you back on post #4, but I had it as a folder action rather then a droplet.

So lets assume for a second you drop the following items on the droplet

Hard Drive:Users:Kevin:Desktop:file1.txt
Hard Drive:Users:Kevin:Desktop:Test:
Hard Drive:Users:Kevin:Desktop:Test2: – This one contains two files file2.jpg and file3.psd
Hard Drive:Users:Kevin:Desktop:Test3:Test4: – This one contains file5.tif

This droplet will return a MasterList as follows:

MasterList {“Hard Drive:Users:Kevin:Desktop:file1.txt”, “Hard Drive:Users:Kevin:Desktop:Test2:file2.jpg”, “Hard Drive:Users:Kevin:Desktop:Test2:file3.psd”, “Hard Drive:Users:Kevin:Desktop:Test3:Test4:file5.tif”}

Is that what you want? If not please give actual examples like this because I for the life of me don’t know what you mean if I’m wrong again :smiley:

property MasterList : {}

on open fileList
	set MasterList to {}
	repeat with thisItem in fileList
		buildMasterList(thisItem)
	end repeat
end open

on buildMasterList(thisItem)
	if (folder of (info for thisItem)) then
		tell application "Finder" to set subItems to items of folder thisItem
		repeat with anItem in subItems
			buildMasterList(anItem as alias)
		end repeat
	else
		set end of MasterList to thisItem
	end if
end buildMasterList

I haven’t followed carefully, but it seemed that Kevin wanted a flat list of all the files dropped as an alias list, and this script does it for me:


property theDropped : {}
on open theDrops
	repeat with D in theDrops
		tell application "Finder"
			if (contents of D as string) ends with ":" then
				try
					set theDropped to theDropped & (files of (entire contents of D) as alias list)
				on error -- only one file inside
					set theDropped to theDropped & (files of (entire contents of D) as alias as list)
				end try
			else
				set theDropped to theDropped & D
			end if
		end tell
	end repeat
end open

I think that’s what he wanted as well, though the ability to look through a folders subfolders for files as well.

Files of entire contents does that, James. When I drop a mishmash of files, files in folders, and files in folders in folders on that droplet, I get back a flat list of all the files as aliases. The names of the folders they were in is in the alias, and so is easily extracted by looking at text items of the alias with the astid set to “:”. Not so? Does he want a list of the folder hierarchy too?

I’ll be damned. Well that beats going through a processing routine. =) I was under the impression that it was just going to return the files themselves without digging through folders.

Loopless is always better if you have to use the Finder to do something. In a repeat loop, every cycle is a slow call to the Finder, in a “files (or folders) of entire contents of pathToBlah as alias list” you are using one instruction for everything in the container, so the only repeat required is to process the contents of theDropped list. “Entire contents – as alias list” has a bug; if there is only one file in a folder it is applied to, it errors and you have to use “as alias as list” which won’t work for more than one file. That’s the reason for the try block. To get a flat file, I have to concatinate rather than set the end of … to.

Gotta run to a meeting but rather that make you poor folks conjecture all morning… :wink:

I think Adam has it here…a list of aliases sans the folders for any combo of files and folders dropped on the droplet, including subfolders. He also captured an error that would have driven me bonkers in testing. :wink:

I won’t have time to try it until later at best, but I think this is what I was going for. THANKS!

EDIT: Oh and meant to mention…I’m using the need to update this file fixing script of mine to learn better technique and more efficient ways of doing things. It works “as is” but I want to make it work better, more efficient, more “standard” than any workarounds I’m using. Just in case everyone was curious. I learn better from you folks than all the books I have at my disposal. :wink:

Hi,

One thing you have to think about when using alias references and rename items. If you rename a file, then the alias reference will point to the new file name. Now, if you add a file with the old name, the alias reference will point to the new file. For instance, say you have a file named aaa.pdf and a alias reference pointing to this file. You rename the file zzz.pdf. Two days later, the user adds a file named aaa.pdf, the alias reference now jumps to this file and is referencing the wrong item. You need to update the reference or use paths.

gl,