Rename folders and items contained therein

Hello.

This script aims to add a string to the end of the selected items in the Finder.

on appStringEndOfNamItem(sel, _text) 
	tell application "System Events"
		repeat with x from 1 to count sel
			set ext to name extension of item x of sel
			set nCompl to name of (item x of sel)
			
			--	if kind of item x of sel is "folder" then
			if nCompl contains "." then
				set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "."}
				set nBas to (text items 1 thru -2 of nCompl) as text
				set name of item x of sel to (nBas & _text & "." & ext)
				set AppleScript's text item delimiters to ATID
			else
				set name of item x of sel to (nCompl & _text)
			end if
		end repeat
		
		return
	end tell
end appStringEndOfNamItem

If the selection is of items of the same type (files or folders), it works correctly.

However, if the selection simultaneously covers a folder and the files/subfolders contained within that folder, an error occurs because after the folder name changes, the initial path reference of the selected selection items has changed (Cannot get the alias “Macintosh HD:Users…”).

I can’t figure out how to fix this situation or how to do a different approach.

Thanks in advance for any help.

Not sure that this is the most efficient approach but you could separate your selection into files and folders and then process all the files first. As I paste it here, I think it would probably be better if the first handler didn’t call the second one and instead left that to the base script but it works for me as is.

Basically, the separate(selList, appendText) handler takes the selection and the text to be added. It checks the type of each item and puts each into a file or folder list. It then calls the second handler, once for the files and then again for the folders (and it passes on the text to append). FWIW, when I swap the order for processing the two lists, it still works. I guess that the script is dynamically applying the new path to the files.

tell application "Finder"
	set selList to selection as alias list
	set appendText to " _more"
	my separate(selList, appendText) -- separate selection into files and folders
end tell

on separate(selList, appendText)
	set fileList to {}
	set folderList to {}
	tell application "System Events"
		repeat with eachFF in selList
			if kind of eachFF is not "Folder" then
				set end of fileList to contents of eachFF -- list of files
			else
				set end of folderList to contents of eachFF -- list of folders
			end if
		end repeat
	end tell
	
	appStringEndOfNamItem(fileList, appendText & ".")
	appStringEndOfNamItem(folderList, appendText)
	
	-- return folderList -- for testing
end separate

on appStringEndOfNamItem(fList, _text) -- each file, then each folder
	tell application "System Events"
		repeat with eachAlias in fList
			
			set nCompl to name of eachAlias
			set ext to name extension of eachAlias
			set nDisp to displayed name of eachAlias
			set nOff to offset of ext in nDisp
			
			if nOff is 0 then
				set shortName to nDisp
			else
				set shortName to text 1 thru (nOff - 2) of nDisp
			end if
			
			set name of eachAlias to shortName & _text & ext
			
		end repeat
	end tell
end appStringEndOfNamItem

The following is a slight variation on the approach suggested by Mockman. It reorders the list of folders and files putting files first.

-- revised 2023.03.09

tell application "Finder"
	set sel to reverse of (selection sort by name)
	set renameList to {}
	repeat with anItem in sel
		set anItem to contents of anItem
		if kind of anItem is "Folder" then
			set end of renameList to anItem as alias
		else
			set beginning of renameList to anItem as alias
		end if
	end repeat
end tell

appStringEndOfNamItem(renameList, " OK")

on appStringEndOfNamItem(sel, _text)
	tell application "System Events"
		repeat with x from 1 to count sel
			set ext to name extension of item x of sel
			set nCompl to name of (item x of sel)
			
			--	if kind of item x of sel is "folder" then
			if nCompl contains "." then
				set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "."}
				set nBas to (text items 1 thru -2 of nCompl) as text
				set name of item x of sel to (nBas & _text & "." & ext)
				set AppleScript's text item delimiters to ATID
			else
				set name of item x of sel to (nCompl & _text)
			end if
		end repeat
		
		return
	end tell
end appStringEndOfNamItem

1 Like

That looks better than mine. I thought about trying the end/beginning thing but wasn’t sure if it would work and didn’t want to sidetrack myself.

There was an error in my script when the user selected a folder and then a folder within that folder in recent versions of macOS. I’ve revised my script above which hopefully fixes that.

Thank you very much @Mockman and @peavine for your willingness to help.

Unfortunately none of the options gives a valid solution. They all fail when trying to rename the folders and the items they contain (files and even subfolders).

I have tried to follow each of your strategies, trying to make changes according to the situation in which the error is detected, but I have not achieved good results.
I think the reason is the changes to the path strings of the initial selection items; specifically the folder names.

I think this is what @Mockman’s approach is all about, treating folders in separate lists and files in separate lists, with changes to folder names first and changes to the names of the items contained in the folders last.

The @peavine approach is similar, but using a single list with the folder names at the front, so that when the items in the list are processed, it is the folder names that are changed first and then the names of the items contained in them (files and subfolders, further increasing complexity if we take into account the possible existence of subfolders).

Although the approach seems correct, the results are not satisfactory. You are probably very close to the solution.

Thanks for your willingness to help with your time and knowledge.

DJUNQUERA. I retested my suggestion and it works as expected on my Ventura computer. It renames folders and files selected in a Finder window and does not rename folders and files not selected. Perhaps you could explain what happens when the script “fails” in your testing.

BTW, the title and a few sentences in your first post as quoted below seem a bit contradictory. Your script in post 1 seems designed to rename selected items only and that’s what my script does. Perhaps you could further explain your request:

Rename folders and items contained therein
This script aims to add a string to the end of the selected items in the Finder.
If the selection is of items of the same type (files or folders), it works correctly.

@DJUNQUERA

If AppleScriptObjC can be an option for you, try this:

use framework "Foundation"
use framework "AppKit"
use scripting additions

global theResult

set theSuffix to "_suffix"

-- get the contents of the selected folder
set theURL to current application's NSURL's fileURLWithPath:"/Users/zerafio/Desktop/aaa"
set theArray to (current application's NSFileManager's defaultManager()'s enumeratorAtURL:theURL includingPropertiesForKeys:(missing value) options:6 errorHandler:(missing value))'s allObjects() -- to ignore subfolders set options to 7 

-- get all files
set thePred to current application's NSPredicate's predicateWithFormat:"pathExtension != %@" argumentArray:{""}
set theFiles to theArray's filteredArrayUsingPredicate:thePred

-- get all folders
set thePred to current application's NSPredicate's predicateWithFormat:"pathExtension == %@" argumentArray:{""}
set theFolders to theArray's filteredArrayUsingPredicate:thePred

-- append suffixes starting with files, ending with folders in reverse order
my appendSuffix:theSuffix array:theFiles
my appendSuffix:theSuffix array:(theFolders's reverseObjectEnumerator()'s allObjects())

-- and finally, if you want to rename the top folder
my appendSuffix:theSuffix array:{theURL}

-- renaming handler
on appendSuffix:theSuffix array:theArray
	repeat with anURL in theArray
		set theExt to anURL's pathExtension()
		set thePath to ((anURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:theSuffix)'s stringByAppendingPathExtension:theExt)
		set newURL to (current application's NSURL's fileURLWithPath:thePath)
		set {theResult, theError} to (current application's NSFileManager's defaultManager()'s moveItemAtURL:anURL toURL:newURL |error|:(reference))
		if theResult = missing value then error theError's localizedDescription() as text
	end repeat
end appendSuffix:array:

Thank you very much, @ionah, for your reply.

The following errors occur and it is not possible to save the file, although it is possible to run it from the Script Editor.

“The document “Untitled 59.scpt” could not be saved as “New rename.scpt". C and Objective-C pointers cannot be saved in scripts”.

“Compiling the script will reset the property values and may resolve this issue”.

On the other hand, the script does not perform the desired action, but its action extends to the entire contents of the folder specified by the user (all files, subfolders and their contents), instead of being limited to the items selected by the user.

My knowledge of AppleScript is quite rudimentary, but my ignorance of AppleScriptObjC is absolute.
However, I can take advantage of your script when I need the action it performs; what I won’t be able to do is enjoy reading your code.

I reiterate my sincere thanks.

First of all I suggest to download the free version of Script Debugger.
You will get rid of many annoying behaviors like being unable to save AppleScriptObjC scripts.

In your first demand, it was not clear you wanted to perform the task only on selected files and folders.

Try this:

use framework "Foundation"
use framework "AppKit"
use scripting additions

set theSuffix to "_suffix"

tell application "Finder" to set theSelection to selection as alias list
set theArray to current application's NSArray's arrayWithArray:theSelection

-- get all files
set thePred to current application's NSPredicate's predicateWithFormat:"pathExtension != %@" argumentArray:{""}
set theFiles to theArray's filteredArrayUsingPredicate:thePred

-- get all folders
set thePred to current application's NSPredicate's predicateWithFormat:"pathExtension == %@" argumentArray:{""}
set theFolders to theArray's filteredArrayUsingPredicate:thePred

-- append suffixes starting with files, ending with folders in reverse order
my appendSuffix:theSuffix array:theFiles
my appendSuffix:theSuffix array:(theFolders's reverseObjectEnumerator()'s allObjects())

-- renaming handler
on appendSuffix:theSuffix array:theArray
	repeat with anURL in theArray
		set theExt to anURL's pathExtension()
		set thePath to ((anURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:theSuffix)'s stringByAppendingPathExtension:theExt)
		set newURL to (current application's NSURL's fileURLWithPath:thePath)
		set {theResult, theError} to (current application's NSFileManager's defaultManager()'s moveItemAtURL:anURL toURL:newURL |error|:(reference))
		if theResult = missing value then error theError's localizedDescription() as text
	end repeat
end appendSuffix:array:


MacOS 12.6.3 (21G419)
Finder 12.5 (1440.5.1)
Script Debugger 8.0.5 (8A61)

1 Like

Hello @ionah

I’m sorry I wasn’t clear enough in my presentation so that there were no misunderstandings when I read it.

Your script works great.
There were no problems with the Script Editor compiling it and saving it.

I tried to test it with the most absurd file selections I could think of and it worked perfectly. It really is a “Magic Renamer”.

It’s a pity that it’s not written with a simple AppleScript and that I can’t follow its algorithm. It is for this reason that, although your script meets the objectives perfectly, I will not consider this post closed, in the confidence that with the help of members of this forum it will also be achieved using AppleScript.

Thank you very much.

DJUNQUERA. You are a hard man to please :slight_smile:

The following is about as simple as it gets, and it works fine in my testing.

set theText to "_suffix"
set TID to text item delimiters
set text item delimiters to {"."}

tell application "Finder"
	set selectedItems to reverse of (selection sort by name)
	set renameList to {}
	repeat with anItem in selectedItems
		if kind of anItem is "Folder" then
			set end of renameList to anItem as alias
		else
			set beginning of renameList to anItem as alias
		end if
	end repeat
	
	repeat with anItem in renameList
		set itemExtension to name extension of anItem
		set itemName to name of anItem
		if itemExtension > "" then
			set nameNoExtension to text 1 thru text item -2 of itemName
			set name of anItem to nameNoExtension & theText & "." & itemExtension
		else
			set name of anItem to itemName & theText
		end if
	end repeat
end tell

set text item delimiters to TID

Hi @peavine

Yes, you are right that the content of my first message and some sentences in a later one can be misleading.

The aim I am looking for is to rename (for example by adding a string at the end) both the selected files in a folder and the name of the folder itself that contains them.

It is true that in another message I mentioned the possibility that the action could be extended to some other selected files contained in subfolders of the first folder. It was not my intention to create confusion with that, but rather an assumption of mine that if the case raised in the first message was solved, maybe the solution could also be extended to selected files located in subfolders of the first mentioned folder.

I tried your first version of the script and told you that I found bugs in the execution. You replied that you were working on the detected bugs.

So far I had not tested your first corrected script nor the last one I just saw.

The first corrected script fails very often and rarely achieves the goal.

on appStringEndOfNamItem(sel, _text)
	tell application "System Events"
		repeat with x from 1 to count sel
			set ext to name extension of item x of sel -- error  -- The compiler highlights "name extension".
			--System Events has detected an error: Unable to obtain aliases "Macintosh HD:Users:…".
			-- However, sometimes it works well. It has erratic behaviour.
			set nCompl to name of (item x of sel)

The latest version of your script has a much higher hit rate, but it also fails sometimes. It is able to succeed even when the selection simultaneously spans the base folder, files in the base folder, even sub-folder and selected files located in the sub-folder.

	repeat with anItem in renameList
		set itemExtension to name extension of anItem -- error.   Get text appended to the end of the folder name.
		-- Finder has detected an error: Cannot get alias "Macintosh HD:Users:...".
		--
		set itemName to name of anItem
		if itemExtension > "" then
			set nameNoExtension to text 1 thru text item -2 of itemName
			set name of anItem to nameNoExtension & theText & "." & itemExtension
		else
			set name of anItem to itemName & theText
		end if
	end repeat

Errors, when they occur, are flagged by the compiler when handling the file extension name.

I made some screenshots where you can see in the Finder the errors in the results, but I don’t know how to send them to you through the post.

Finally, I thought I saw in some of your comments some anger on your part towards me.

Please forgive me if you have felt offended in any way. It is not my intention to offend you, quite the contrary.

Thank you very much for your help and time.

DJUNQUERA. What you perceive as anger is probably some measure of frustration on my part because I didn’t know what precisely you wanted the script to do and because you didn’t provide meaningful feedback as to the circumstances under which my script didn’t work. If I recall correctly, this has been an issue between us before, so I’ll move on.

I recognise that because of my limitations in the English language (I am completely unfamiliar with the language and depend entirely on translators), although I try to be as accurate as possible in my mother tongue before translating, it is possible that there may be shortcomings that could lead to misunderstandings in the idea of what I really intend to express. I apologise if such a situation has arisen.

There are two aspects of his latest script on which I am totally lost.

. I don’t understand why you put the whole script under the influence of the “text item delimiters” because, if I understood correctly, they are only of interest for the fragment that reconstructs the name of the files. Maybe this last statement is not correct.

. Another issue that I haven’t even grasped the meaning of is

set selectedItems to reverse of (selection sort by name)

By declaring “.” as text item delimiters, the item names that have extension are split into 2 fragments, but I don’t know how to continue the reasoning, if you have that fact with the statement that the item selection refers to.

I would be grateful if you could help me to understand these two aspects.

Thank you for your generosity.

P.S.: I forgot to ask you if you could tell me how to send screenshots, (which icon in the tools menu to use), as this resource is very interesting for communicating things that can be complicated and inaccurate with language.

The answer given by @ionah gives correct results every time, but it is written in appleScriptObjC.

However, the alternative given in appleScript sometimes produces errors, as discussed in post 13/15.

Hi @DJUNQUERA… Part of the problem is that nobody except you knows what files and folders you’re selecting or what is happening so we can’t test our scripts against the same situation.

This is kind of a weird post. My previous post notwithstanding, I took another look at my script. I improved how it handles files without extensions and also discovered something odd in that some of the files were renamed but would not update to show them.

I have a finder window open that contains 3 folders and numerous files. One of those folders has a sub-folder

Finder window

    folder1A
    folder1B
    folder1C
        folder2C
            file2C1.txt
            file2C2.stuf
            file2C3


When I ran my original script, it generally renamed every selected file or folder.

However, if I selected files in folder2C but not folder2C itself (and a bunch of other files), then something strange happened. For files in the main window or any level 1 folder it didn’t matter whether the containing folder was also selected. Or, if I selected only a handful of files. It was only when multiple levels deep, with numerous files and folders selected (varies but >10) but the containing folder not selected that this happened.

What happened to those files was that they would not appear to be renamed. When I opened Get Info windows, they were not renamed. When I tried to open them (double-click or right-click Open), they would not open (while all the other renamed files opened normally). Closing and opening the folder made no difference. However, if I used TextEdit’s File > Open… dialogue to try and open the difficult files, the updated file name was shown and they would open.

When trying to track the selection I discovered that changing the file label for those files would cause the names to be updated. After some playing around, I found that if I ran a truncated form of my script —without calling the appStringEndOfNamItem() handler— then the file names would also update. Some more experimenting revealed that the file names would update when ‘finder activate’ and ‘finder selection’ lines were added at the end of the separate() handler.

Finally, I tried putting ‘activate/selection’ in the opening Finder tell block. If placed at the beginning the file names would not update but when placed at the end of the tell block then the names would update.

Anyway, my apologies for the long ramble but it’s an odd situation and I’m curious whether anyone has any ideas as to what the cause might be. Let me know if any other information would be useful. It’s a bit of a sidetrack so I’ll post my revised script in another post.

Here is the revised script that works even with the situation described above.

tell application "Finder"
	set selList to selection as alias list
	set appendText to " _more"
	my separate(selList, appendText) -- separate selection into files and folders
	activate
	selection
end tell

on separate(selList, appendText)
	set fileList to {}
	set folderList to {}
	tell application "System Events"
		repeat with eachFF in selList
			if kind of eachFF is not "Folder" then
				set beginning of fileList to contents of eachFF -- list of files
			else
				set beginning of folderList to contents of eachFF -- list of folders
			end if
		end repeat
	end tell
	
	appStringEndOfNamItem(fileList, appendText)
	appStringEndOfNamItem(folderList, appendText)
	
	-- return fileList -- for testing	
end separate

on appStringEndOfNamItem(fList, _text) -- each file, then each folder
	tell application "System Events"
		repeat with eachAlias in fList
			
			set fName to name of eachAlias
			set fDisplayed to displayed name of eachAlias
			set fExt to name extension of eachAlias
			
			if fExt is "" then
				set shortName to fDisplayed -- file without extension
			else -- file with extension
				set shortName to text 1 thru (-(length of fExt) - 2) of fDisplayed
				set fExt to "." & fExt
			end if
			
			set name of eachAlias to shortName & _text & fExt
			
		end repeat
	end tell
end appStringEndOfNamItem
1 Like

Hi @Mockman

I’ve been testing your alternative on up to 5 levels of subfolders, trying as many variations of selection as my imagination has allowed.
There may be situations I haven’t tried, but the result of your script is a brilliant success.

I think that once you have found the solution to focus an arbitrary selection of items within a folder (including several levels of subfolders) you have a great tool to perform more complex operations than renaming.

Thank you very much for your time and knowledge.

Congratulations for having found a solution to an issue that was initially much simpler (modifying a folder and a partial selection of the files contained in it) and finally extended to any selection of files and subfolders that the base folder might contain.