Changing names of item in multi-level folders?

G’day from Oz

To help someone who wanted to re-name photos of the same name, I’ve written a script to do the job.

Trouble is, it’s limited to items in folders one level deep :frowning:

I imagine that it’s possible to write the algorythm to include re-iterated multiple levels of folders, but it’s beyond my limited Applescript knowledge.

Would anyone care to give me some pointers please?

Regards

Santa



(*    Item renamer

Use as a Folder Action

When items (except for applications) are dropped on the folder,
they are renamed and stored in the enclosed folder '* Re-named Items'.

Version 2.0 added the ability to rename files in folders one
layer deep, and keep them in the folders.

When 'Property AlwaysReName : false' is set, then items are only 
re-named if an item of the same name already exists in the folder 
'*Re-named Items', or exists in a folder inside of '*Re-named Items'

If you ALWAYS want to re-name EVERY item, then set the
'Property AlwaysReName : true'

By Santa
Version 2.1
*)


property AlwaysReName : false
property FolderName : "* Re-named Items"
property FirstPass : true

global AllTheExistingNames
global AllTheNewNames

on adding folder items to this_folder after receiving added_items
	set SelectShiftedItems to {}
	try
		tell application "Finder"
			set temp to name of item 1 of added_items -- this bit ensures creation of new folder doesn't create loops
			if temp is not FolderName then
				if not (exists folder FolderName of this_folder) then
					make new folder at this_folder with properties {name:FolderName}
				end if
				set the destination_folder to folder FolderName of this_folder as alias
				
				set temp to every item of folder FolderName of this_folder
				set AllTheExistingNames to my AddUpTheItems(temp)
				
				set temp to added_items
				set AllTheNewNames to my AddUpTheItems(temp)
				
				(* We can't use 'Repeat with TheItem in added_items', because
			we're altering the added_items by name changing. Instead we use a counting loop. *)
				repeat with Counter from 1 to number of items in added_items
					set TheItem to item Counter of added_items
					if kind of (info for TheItem as alias) is not in {"Folder", "Application"} then
						set NewFileName to my GoAndRename(TheItem)
						--set CheckFolder to container of TheItem
						if FolderName is not in (container of TheItem as string) then
							set the name of TheItem to NewFileName
							move document file NewFileName of this_folder to destination_folder
							set SelectShiftedItems to SelectShiftedItems & NewFileName
						end if
					else
						-- If we've struck a folder, re-name, move it, then rename contents
						if kind of (info for TheItem as alias) is "Folder" then
							-- Check if we need to rename the folder
							-- but first, check if folder is still in re-naming folder?
							if FolderName is not in (container of TheItem as string) then
								set tempName to my GoAndRename(TheItem)
								set the name of TheItem to tempName
								move TheItem to destination_folder
								-- Always rename them? then refresh the Existing name list
								if AlwaysReName then
									set temp to every item of folder FolderName of this_folder
									set AllTheExistingNames to my AddUpTheItems(temp)
								end if
								set SelectShiftedItems to SelectShiftedItems & name of TheItem
								set CycleContents to items of folder tempName in folder destination_folder
								
								-- This bit here is a problem, it's restricted to one level deep
								
								repeat with FolderCounter from 1 to (number of items in CycleContents)
									set TheFolderItem to item FolderCounter of TheItem
									if kind of (info for TheFolderItem as alias) is not in {"Folder", "Application"} then
										set NewFileName to (my GoAndRename(TheFolderItem))
										set the name of (TheFolderItem) to NewFileName
									end if
								end repeat
							end if
						end if
					end if
				end repeat
				if SelectShiftedItems ≠ {} then
					open destination_folder
					select (every item of destination_folder whose (name is in SelectShiftedItems))
				end if
			end if
		end tell
	end try
end adding folder items to

on AddUpTheItems(temp)
	try
		tell application "Finder"
			set AllTheNames to {}
			repeat with TheExistingItems in temp
				if kind of (info for TheExistingItems as alias) is "Folder" then
					if name of TheExistingItems ≠ FolderName then
						set AllTheNames to AllTheNames & name of TheExistingItems
						repeat with ExistingFolderItems in TheExistingItems
							if kind of (info for ExistingFolderItems as alias) is not in {"Folder", "Application"} then
								set end of AllTheNames to name of (info for ExistingFolderItems as alias)
							end if
						end repeat
					end if
				else
					if (kind of (info for TheExistingItems as alias)) is not "Application" then
						set end of AllTheNames to name of (info for TheExistingItems as alias)
					end if
				end if
			end repeat
		end tell
	end try
	return AllTheNames
end AddUpTheItems

on GoAndRename(TheFolderItem)
	try
		tell application "Finder"
			set temp3 to ""
			set NameItem to TheFolderItem as alias
			set TheName to (displayed name of (info for (NameItem)))
			if (extension hidden of (info for (NameItem))) = false then
				set temp3 to "." & (name extension of (info for (NameItem)))
				-- Now set main string for name, minus extension
				if temp3 = ".missing value" then
					set temp3 to ""
				else
					set TheName to my replace_chars(TheName, temp3, "")
				end if
			end if
			set AddToName to 1 -- The 'add-on to name' item
			set DontAddOn to true -- To ensure first repeat cycle uses original name
			set FirstPass to true -- stops 2 cycles of adding to names, cause renaming triggers another cycle
			repeat
				if DontAddOn and not AlwaysReName then
					set NewName to TheName & temp3 -- Set back to original on first repeat
				else
					set NewName to TheName & " " & (AddToName as string) & temp3
				end if
				set RenameFlag to false
				(* Now we have to check two states...
						1. Does the re-named file exist in the destination folder,
						2. is there a conflict in the present folder if we re-name.
						*)
				if NewName is not in AllTheExistingNames then
					if my CheckAllTheNewItems(NewName) < 2 then
						set RenameFlag to true
					else
						(* and if number 2 above is false, we still need to allow
						for the  'Always Rename' state being false.
						*)
						if DontAddOn then
							set RenameFlag to true
						end if
					end if
				end if
				if RenameFlag then
					exit repeat
				else
					set AddToName to AddToName + 1
					set DontAddOn to false
				end if
			end repeat
		end tell
	end try
	set AllTheExistingNames to AllTheExistingNames & NewName
	return NewName
end GoAndRename

on CheckAllTheNewItems(CheckName)
	set y to 0
	-- we need to fiddle with FirstPass in case the item has a unique name,
	-- and just needs shifting. Otherwise, if a dropped file exists with the 
	-- new intended name then up y to 2
	repeat with x from 1 to number of items in AllTheNewNames
		set test to item x of AllTheNewNames
		if test is equal to CheckName then set y to y + 1
	end repeat
	if FirstPass and y = 1 then
		set y to 2
		set FirstPass to false
	end if
	return y
end CheckAllTheNewItems

on replace_chars(this_text, search_string, replacement_string)
	set tempdelimiters to AppleScript's text item delimiters
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to tempdelimiters
	return this_text
end replace_chars


Model: G5 1.8 GHz
AppleScript: 2.1.1
Browser: Safari 419.3
Operating System: Mac OS X (10.4)

Here’s a script to recursively search for files inside of a folder i.e. it will look inside a folder and also inside all of the folders within that folder and so on. The interesting part is it calls the subroutine from within the subroutine.


global theItemList
set theItemList to {}

set thefolder to choose folder
set recursiveFolderList to getItemNames(thefolder)

on getItemNames(aFolder)
	tell application "Finder"
		set theFolderItems to (get items of aFolder)
	end tell
	repeat with anItem in theFolderItems
		tell application "Finder"
			if kind of anItem is not "folder" then
				set continueProc to false
				set anItemPath to anItem as string
				set theItemList to theItemList & anItemPath
			else
				set continueProc to true
			end if
		end tell
		if continueProc is true then
			set anItemPath to anItem as string
			set theItemList to theItemList & anItemPath
			getItemNames(anItem) --> this calls the subroutine from within it!
		end if
	end repeat
	return theItemList
end getItemNames

Thanks for that Regulus.

It works a treat compiling the existing files.

However I’ve struck a problem when re-naming files.

As soon as the loop get a re-name, it errors.

I’ve tried a counting loop, but on each recursive cycle the counter resets.

The error bit is listed, with the whole file below.

Can anyone advise me if there’s a fix please

Regards

Santa

on RenameInFolders(aFolder)
	tell application "Finder"
		set theFolderItems to (get items of aFolder)
	end tell
	repeat with anItem in theFolderItems
		tell application "Finder"
			if kind of (info for anItem as alias) is not "Application" then
				set continueProc to false
				set NewFileName to (my GoAndRename(anItem))
				
				-- This bit here stuffs up bad. As soon as re-named, the loop errors
				set the name of (anItem) to NewFileName
				
				set end of AllTheExistingNames to NewFileName
				if kind of anItem is "folder" then set continueProc to true
			end if
		end tell
		if continueProc is true then
			my RenameInFolders(anItem)
		end if
	end repeat
end RenameInFolders


(*    Item renamer

Use as a Folder Action

When items (except for applications) are dropped on the folder,
they are renamed and stored in the enclosed folder '* Re-named Items'.

Version 2.2 added the ability to rename folders, & files in folders, any amount of
layers deep, and keep them in the folders.

When 'Property AlwaysReName : false' is set, then items are only 
re-named if an item of the same name already exists in the folder 
'*Re-named Items', or exists in a folder inside of '*Re-named Items'

If you ALWAYS want to re-name EVERY item, then set the
'Property AlwaysReName : true'

By Santa
Version 2.2
*)


property AlwaysReName : false
property FolderName : "* Re-named Items"
property FirstPass : true

global AllTheExistingNames
global AllTheNewNames
global AllTheNames

on adding folder items to this_folder after receiving added_items
	set SelectShiftedItems to {}
	try
		tell application "Finder"
			set temp to name of item 1 of added_items -- this bit ensures creation of new folder doesn't create loops
			if temp is not FolderName then
				if not (exists folder FolderName of this_folder) then
					make new folder at this_folder with properties {name:FolderName}
				end if
				set the destination_folder to folder FolderName of this_folder as alias
				
				set AllTheNames to {}
				set AllTheExistingNames to my AddUpTheItems(destination_folder)
				
				set AllTheNames to {}
				set AllTheNewNames to my AddUpTheItems(added_items)
				
				(* We can't use 'Repeat with TheItem in added_items', because
			we're altering the added_items by name changing. Instead we use a counting loop. *)
				repeat with Counter from 1 to number of items in added_items
					set TheItem to item Counter of added_items
					if kind of (info for TheItem as alias) is not in {"Folder", "Application"} then
						set NewFileName to my GoAndRename(TheItem)
						--set CheckFolder to container of TheItem
						if FolderName is not in (container of TheItem as string) then
							set the name of TheItem to NewFileName
							move document file NewFileName of this_folder to destination_folder
							set SelectShiftedItems to SelectShiftedItems & NewFileName
						end if
					else
						-- If we've struck a folder, re-name, move it, then rename contents
						if kind of (info for TheItem as alias) is "Folder" then
							-- Check if we need to rename the folder
							-- but first, check if folder is still in re-naming folder?
							if FolderName is not in (container of TheItem as string) then
								set tempName to my GoAndRename(TheItem)
								set the name of TheItem to tempName
								move TheItem to destination_folder
								-- Always rename them? then refresh the Existing name list
								if AlwaysReName then
									set temp to every item of folder FolderName of this_folder
									set AllTheExistingNames to my AddUpTheItems(temp)
								end if
								set SelectShiftedItems to SelectShiftedItems & name of TheItem
								my RenameInFolders(TheItem)
							end if
						end if
					end if
				end repeat
				if SelectShiftedItems ≠ {} then
					open destination_folder
					select (every item of destination_folder whose (name is in SelectShiftedItems))
				end if
			end if
		end tell
	end try
end adding folder items to

on AddUpTheItems(aFolder)
	try
		tell application "Finder"
			set theFolderItems to (get items of aFolder)
		end tell
		repeat with TheExistingItems in theFolderItems
			tell application "Finder"
				if kind of TheExistingItems is not in {"Application", "folder"} then
					set continueProc to false
					set end of AllTheNames to name of (info for TheExistingItems as alias)
				else
					if kind of TheExistingItems is "folder" then
						set end of AllTheNames to name of (info for TheExistingItems as alias)
						set continueProc to true
					end if
				end if
			end tell
			if continueProc is true then
				my AddUpTheItems(TheExistingItems)
			end if
		end repeat
	end try
	return AllTheNames
end AddUpTheItems

on RenameInFolders(aFolder)
	tell application "Finder"
		set theFolderItems to (get items of aFolder)
	end tell
	repeat with anItem in theFolderItems
		tell application "Finder"
			if kind of (info for anItem as alias) is not "Application" then
				set continueProc to false
				set NewFileName to (my GoAndRename(anItem))
				
				-- This bit here stuffs up bad. As soon as re-named, the loop errors
				set the name of (anItem) to NewFileName
				
				set end of AllTheExistingNames to NewFileName
				if kind of anItem is "folder" then set continueProc to true
			end if
		end tell
		if continueProc is true then
			my RenameInFolders(anItem)
		end if
	end repeat
end RenameInFolders

on GoAndRename(TheFolderItem)
	try
		tell application "Finder"
			set temp3 to ""
			set NameItem to TheFolderItem as alias
			set TheName to (displayed name of (info for (NameItem)))
			if (extension hidden of (info for (NameItem))) = false then
				set temp3 to "." & (name extension of (info for (NameItem)))
				-- Now set main string for name, minus extension
				if temp3 = ".missing value" then
					set temp3 to ""
				else
					set TheName to my replace_chars(TheName, temp3, "")
				end if
			end if
			set AddToName to 1 -- The 'add-on to name' item
			set DontAddOn to true -- To ensure first repeat cycle uses original name
			set FirstPass to true -- stops 2 cycles of adding to names, cause renaming triggers another cycle
			repeat
				if DontAddOn and not AlwaysReName then
					set NewName to TheName & temp3 -- Set back to original on first repeat
				else
					set NewName to TheName & " " & (AddToName as string) & temp3
				end if
				set RenameFlag to false
				(* Now we have to check two states...
						1. Does the re-named file exist in the destination folder,
						2. is there a conflict in the present folder if we re-name.
						*)
				if NewName is not in AllTheExistingNames then
					if my CheckAllTheNewItems(NewName) < 2 then
						set RenameFlag to true
					else
						(* and if number 2 above is false, we still need to allow
						for the  'Always Rename' state being false.
						*)
						if DontAddOn then
							set RenameFlag to true
						end if
					end if
				end if
				if RenameFlag then
					exit repeat
				else
					set AddToName to AddToName + 1
					set DontAddOn to false
				end if
			end repeat
		end tell
	end try
	set AllTheExistingNames to AllTheExistingNames & NewName
	return NewName
end GoAndRename

on CheckAllTheNewItems(CheckName)
	set y to 0
	-- we need to fiddle with FirstPass in case the item has a unique name,
	-- and just needs shifting. Otherwise, if a dropped file exists with the 
	-- new intended name then up y to 2
	repeat with x from 1 to number of items in AllTheNewNames
		set test to item x of AllTheNewNames
		if test is equal to CheckName then set y to y + 1
	end repeat
	if FirstPass and y = 1 then
		set y to 2
		set FirstPass to false
	end if
	return y
end CheckAllTheNewItems

on replace_chars(this_text, search_string, replacement_string)
	set tempdelimiters to AppleScript's text item delimiters
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to tempdelimiters
	return this_text
end replace_chars

Model: G5 1.8 GHz
AppleScript: 2.1.1
Browser: Safari 419.3
Operating System: Mac OS X (10.4)

G’day again

It’s ok, I saved the paths and name changes to lists, and after the recursions, simply used the lists to re-name the files.

Thanks

Thus


	on RenameInFolders(aFolder)
	tell application "Finder"
		set theFolderItems to (get items of aFolder)
	end tell
	repeat with anItem in theFolderItems
		tell application "Finder"
			if kind of (info for anItem as alias) is not "Application" then
				set continueProc to false
				set NewFileName to (my GoAndRename(anItem))
				
				set the end of keepPathNames to anItem
				set the end of keepNames to NewFileName
				
				set end of AllTheExistingNames to NewFileName
				if kind of anItem is "folder" then set continueProc to true
			end if
		end tell
		if continueProc is true then
			my RenameInFolders(anItem)
		end if
	end repeat
end RenameInFolders
			

Called and processed from this…


set keepPathNames to {}
								set keepNames to {}
								my RenameInFolders(TheItem)
								repeat with x from 1 to number of items in keepPathNames
									set name of item x of keepPathNames to item x of keepNames
								end repeat