Target last period in filename

Hi,
How can i target the last period in a filename? The script below works fine on filenames which only contains 1 period in filename. But when there are more periods it uses the first period to change the filename.

tell application "Finder"
	set FileList to selection
end tell

repeat with theItem in FileList
	tell application "Finder" to set NameOnly to name of (theItem as alias)
	
	set theText to "{query}"
	set fName to ((items 1 thru ((offset of "." in (get name of theItem)) - 1) of (get name of theItem)) as string) & theText & "." & name extension of theItem
	
	tell application "Finder"
		set the name of theItem to fName
	end tell
	
end repeat

Or would it be possible to change the order, so it starts at the end of the filename?

What would be the best solution?

Thanks.

A reliable solution is to get the position of the dot plus the name extension

tell application "Finder"
	set fileList to selection
end tell

repeat with theItem in fileList
	tell application "Finder" to set {name:Nm, name extension:Ex} to theItem
	set nameOnly to text 1 thru ((get offset of "." & Ex in Nm) - 1) of Nm
	set theText to "{query}"
	set fName to nameOnly & theText
    if Ex is not "" then
           set fName to fName & "." & Ex
    end if
	
	tell application "Finder"
		set the name of (contents of theItem) to fName
	end tell
	
end repeat

Important note: In a repeat with ... in form Itā€™s mandatory to change properties with contents of because the item in the list must be dereferenced.

An alternative is to manipulate the path components with the Foundation framework

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

tell application "Finder"
	set fileList to selection
end tell

repeat with theItem in fileList
	tell application "Finder" to set theURL to URL of theItem
	set fileURL to (current application's NSURL's URLWithString:theURL)
	set nameExtension to (fileURL's pathExtension()) as text
	set nameOnly to (fileURL's URLByDeletingPathExtension)'s lastPathComponent()
	set theText to "{query}"
	set fName to (nameOnly as text) & theText
	if (length of nameExtension) > 0 then
		set fName to fName & "." & nameExtension
	end if
	
	tell application "Finder"
		set the name of (contents of theItem) to fName
	end tell
	
end repeat

Here is a cleaned up version.
I try to use ā€œSystem Eventsā€ over ā€œFinderā€ whenever I can. ā€œFinderā€ can be very slow especially when dealing with large file counts. Itā€™s also very slow at ā€œwhoseā€ clauses

tell application "Finder" to set FileList to selection

set tid to text item delimiters
set text item delimiters to "."
repeat with theItem in FileList
	--set NameOnly to name of theItem
	set theItem to theItem as alias
	set NameOnly to text items of NameOnly
	set theText to "{query}"
	if (count NameOnly) > 1 then -- filename has an extension
		set text item -2 of NameOnly to text item -2 of NameOnly & theText
		set fName to NameOnly as text
	else -- filename is missing an extension
		set fName to (NameOnly as text) & theText
	end if
	tell application "System Events" to set the name of theItem to NameOnly
end repeat
set text item delimiters to tid

Thanks for the quick replies!

@StefanK
This one leaves a period after the query when the script is applied to a folder. Like this ā€˜Folder_name_v1.ā€™
(I know, i asked for filename, so there it works)

@robertfern
Works great thanks, it also fixed the period when applied to a folder name

Is there a way to undo the script when applied? I now delete the added query with a script but it would be much easier if command-z could be used?

I took a different route with this following code.

With 1000 files selected in Finder, this code took 36.16 seconds to complete.

tell application "Finder" to set fileList to selection as alias list

set theText to "{query}"

repeat with thisItem in fileList
	tell application "System Events" to set nameExtension to name extension of thisItem
	
	if nameExtension ā‰  "" then
		set baseName to do shell script "basename -s ." & nameExtension & " " & quoted form of POSIX path of thisItem
		tell application "System Events" to set name of thisItem to baseName & theText & "." & nameExtension
	else
		set baseName to do shell script "basename " & quoted form of POSIX path of thisItem
		tell application "System Events" to set name of thisItem to baseName & theText
	end if
end repeat

However, the code from the post marked as the ā€œSolutionā€ from @robertfern , with that same 1000 files selected, Took 1 minute and 48.30 seconds to complete.

That is a significant difference.

ASObjC is hard to beat when renaming a large number of files. The timing result with the following script with 1000 files was 1.143 second. The timing result with wch1zpinkā€™s script was 6.328 seconds. I donā€™t know why wch1zpink and I are seeing such a big difference with his script (I verified that the files were renamed). My computer is a 2023 Sonoma Mac mini.

use framework "Foundation"
use scripting additions

tell application "Finder" to set fileList to selection as alias list
set theText to " {query}"
set fileManager to current application's NSFileManager's defaultManager()
repeat with aFile in fileList
	set aFile to (current application's NSString's stringWithString:(POSIX path of aFile))
	set fileExtension to aFile's pathExtension()
	set theRenamedFile to ((aFile's stringByDeletingPathExtension()'s stringByAppendingString:theText)'s stringByAppendingPathExtension:fileExtension)
	set {theResult, theError} to (fileManager's moveItemAtPath:aFile toPath:theRenamedFile |error|:(reference))
end repeat
1 Like

I get an error when I run your revised script. I donā€™t see where FilePath is set.

This text is selected:

set the name of FilePath to theItem

This is the error:

error "The variable FilePath is not defined." number -2753 from "FilePath"

Here is a cleaned up version of my script.
On a folder with 1000 files it took just under 2.5 seconds

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

local FileList, PathList, hiddenFiles
--tell application "Finder" to set FileList to selection
tell application "System Events"
	set {PathList, FileList} to {path, name} of files of folder "Mac SSD:Users:robert:Desktop:Test:" --whose visible is true
	set hiddenFiles to path of files of folder "Mac SSD:Users:robert:Desktop:Test:" whose visible is false
end tell

set tid to text item delimiters
set text item delimiters to "."
set theText to "{query}"
repeat with i from 1 to count PathList
	set theItem to (item i of PathList) --as alias
	if theItem is not in hiddenFiles then
		set NameOnly to text items of item i of FileList
		if (count NameOnly) > 1 then -- filename has an extension
			set text item -2 of NameOnly to text item -2 of NameOnly & theText
			set fName to NameOnly as text
		else -- filename is missing an extension
			set fName to (NameOnly as text) & theText
		end if
		set theItem to theItem as alias
		tell application "System Events" to set the name of theItem to fName
	end if
end repeat
set text item delimiters to tid

** EDIT ** - I cleaned it up a little more

@peavine
Thanks, this one is indeed very quick. How can i reverse this script so that it deletes the query? My deletion script is really slow now on 1000 files compared to the appending script.

Tried to change:
stringByAppendingString:theText
to
stringByDeletingString:theText

But that does nothing, I guess that thought was way to easy?!

use framework "Foundation"
use scripting additions

tell application "Finder" to set fileList to selection as alias list
set theText to "{query}"
set fileManager to current application's NSFileManager's defaultManager()
repeat with aFile in fileList
	set aFile to (current application's NSString's stringWithString:(POSIX path of aFile))
	set fileExtension to aFile's pathExtension()
	set theRenamedFile to ((aFile's stringByDeletingPathExtension()'s stringByDeletingString:theText)'s stringByDeletingPathExtension:fileExtension)
	set {theResult, theError} to (fileManager's moveItemAtPath:aFile toPath:theRenamedFile |error|:(reference))
end repeat

Here is a version to remove the text ā€œ{Query}ā€

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

property folderPath : "Mac SSD:Users:robert:Desktop:Test:"

local FileList, PathList, hiddenFiles
--tell application "Finder" to set FileList to selection
tell application "System Events"
	--get files of folder folderPath
	set {PathList, FileList} to {path, name} of files of folder folderPath --whose visible is true
	set hiddenFiles to path of files of folder folderPath whose visible is false
end tell

set tid to text item delimiters
set text item delimiters to "."
set theText to "{query}"
set c to 1 + (length of theText)
repeat with i from 1 to count PathList
	set theItem to (item i of PathList) --as alias
	if theItem is not in hiddenFiles then
		set NameOnly to text items of item i of FileList
		if (count NameOnly) > 1 then -- filename has an extension
			if (text item -2 of NameOnly) ends with theText then
				set text item -2 of NameOnly to text 1 thru -c of text item -2 of NameOnly
			end if
			set fName to NameOnly as text
		else -- filename is missing an extension
			set fName to (NameOnly as text)
			if fName ends with theText then
				set fName to text 1 thru -c of fName
			end if
		end if
		set theItem to theItem as alias
		tell application "System Events" to set the name of theItem to fName
	end if
end repeat
set text item delimiters to tid

@robertfern
The scripts donā€™t work i guess it has something to do with the fixed paths?
"Mac SSD:Users:robert:Desktop:Test:"
Canā€™t they just use the location of the files that need to be modified without a fixed path?

The fixed path is a folder on my Mac with text files in it. You should change it to what you want or you can also query the user to choose a folder at runtime

Here is a version that prompts the user to choose the folder, the text and whether to add or remove the text.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

local FileList, PathList, hiddenFiles, mvAns, folderPath, theText, tid
set myAns to display dialog "Enter text you wish to Add/Remove:" default answer "{Query}" buttons {"Add", "Remove", "Cancel"} cancel button "Cancel" default button "Cancel" with title "Add or Remove text from filenamesā€¦"
set theText to text returned of myAns
set folderPath to (choose folder "Choose a folder to rename the files withinā€¦") as text

tell application "System Events"
	set {PathList, FileList} to {path, name} of files of folder folderPath
	set hiddenFiles to path of files of folder folderPath whose visible is false
end tell

set tid to text item delimiters
set text item delimiters to "."
if button returned of myAns is "Add" then -- add "{Query}"
	repeat with i from 1 to count PathList
		set theItem to (item i of PathList) --as alias
		if theItem is not in hiddenFiles then
			set NameOnly to text items of item i of FileList
			if (count NameOnly) > 1 then -- filename has an extension
				set text item -2 of NameOnly to text item -2 of NameOnly & theText
				set fName to NameOnly as text
			else -- filename is missing an extension
				set fName to (NameOnly as text) & theText
			end if
			set theItem to theItem as alias
			tell application "System Events" to set the name of theItem to fName
		end if
	end repeat
else if button returned of myAns is "Remove" then -- remove "{Query}"
	set c to 1 + (length of theText)
	repeat with i from 1 to count PathList
		set theItem to (item i of PathList) --as alias
		if theItem is not in hiddenFiles then
			set NameOnly to text items of item i of FileList
			if (count NameOnly) > 1 then -- filename has an extension
				repeat while (text item -2 of NameOnly) ends with theText
					--if (text item -2 of NameOnly) ends with theText then
					set text item -2 of NameOnly to text 1 thru -c of text item -2 of NameOnly
					--end if
				end repeat
				set fName to NameOnly as text
			else -- filename is missing an extension
				set fName to (NameOnly as text)
				if fName ends with theText then
					set fName to text 1 thru -c of fName
				end if
			end if
			set theItem to theItem as alias
			tell application "System Events" to set the name of theItem to fName
		end if
	end repeat
end if
set text item delimiters to tid

** EDIT ** - I modified the prompts in the dialog

Sorry, i donā€™t get it? Your previous script (below) didnā€™t use a fix path, right?

tell application "Finder" to set FileList to selection

set tid to text item delimiters
set text item delimiters to "."
repeat with theItem in FileList
	--set NameOnly to name of theItem
	set theItem to theItem as alias
	set NameOnly to text items of NameOnly
	set theText to "{query}"
	if (count NameOnly) > 1 then -- filename has an extension
		set text item -2 of NameOnly to text item -2 of NameOnly & theText
		set fName to NameOnly as text
	else -- filename is missing an extension
		set fName to (NameOnly as text) & theText
	end if
	tell application "System Events" to set the name of theItem to NameOnly
end repeat
set text item delimiters to tid

Why does this new one need a fixed path?

I need to modify the files in whatever location they are at that moment.

Correct, it chose files that were selected in the finder.

Thanks for extending this script as it is now, looks very good. It now does a little to much for my purpose.

Iā€™m using the script in combination with an alfred list filter (with predefined text additions). When i hold down the alt-key it removes the predefined text addition. And no need to give up a location for the files.

Thanks, maybe i (or somebody else) can use it for a different use case.

Here is another version I was able to shrink down.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

local FileList, PathList, hiddenFiles, mvAns, folderPath, theText, theItem, fName, NameOnly, tid

set myAns to display dialog "Enter text you wish to Add/Remove:" default answer "{Query}" buttons {"Add", "Remove", "Cancel"} cancel button "Cancel" default button "Cancel" with title "Add or Remove text from filenamesā€¦"
set theText to text returned of myAns
set folderPath to (choose folder "Choose a folder to rename the files withinā€¦") as text

tell application "System Events"
	set {PathList, FileList} to {path, name} of files of folder folderPath
	set hiddenFiles to path of files of folder folderPath whose visible is false
end tell

set tid to text item delimiters
set text item delimiters to "."
set c to 1 + (length of theText)
repeat with i from 1 to count PathList
	set theItem to (item i of PathList) --as alias
	if theItem is not in hiddenFiles then
		set NameOnly to text items of item i of FileList
		if (count NameOnly) > 1 then -- filename has an extension
			if button returned of myAns is "Add" then -- add "{Query}"
				set text item -2 of NameOnly to text item -2 of NameOnly & theText
			else if button returned of myAns is "Remove" then -- remove "{Query}"
				repeat while (text item -2 of NameOnly) ends with theText
					set text item -2 of NameOnly to text 1 thru -c of text item -2 of NameOnly
				end repeat
			end if
			set fName to NameOnly as text
		else -- filename is missing an extension
			if button returned of myAns is "Add" then -- add "{Query}"
				set fName to (NameOnly as text) & theText
			else if button returned of myAns is "Remove" then -- remove "{Query}"
				set fName to (NameOnly as text)
				if fName ends with theText then
					set fName to text 1 thru -c of fName
				end if
			end if
		end if
		set theItem to theItem as alias
		tell application "System Events" to set the name of theItem to fName
	end if
end repeat
set text item delimiters to tid

vanwoods. Iā€™ve included below a script that will remove the query from files selected in a Finder window. A few comments:

  • In my earlier script I included a space before {query}, and I did the same in this script. Simply remove the space if thatā€™s what you prefer.
  • This and my earlier script overwrite existing files when a selected file is being renamed. This would not appear to be an issue, but I can change the script to prompt or skip if there is an existing file.
  • I tested this script on my Sonoma computer without issue, but you should always have backup copies of the files being renamed.
use framework "Foundation"
use scripting additions

tell application "Finder" to set fileList to selection as alias list
set theText to " {query}" --delete space at beginning if desired
set fileManager to current application's NSFileManager's defaultManager()
repeat with aFile in fileList
	set aFile to (current application's NSString's stringWithString:(POSIX path of aFile))
	set fileExtension to aFile's pathExtension()
	set fileNoExtension to aFile's stringByDeletingPathExtension()
	if (fileNoExtension's hasSuffix:theText) as boolean is true then
		set fileNoExtension to (fileNoExtension's stringByReplacingOccurrencesOfString:theText withString:"")
		set theRenamedFile to (fileNoExtension's stringByAppendingPathExtension:fileExtension)
		set {theResult, theError} to (fileManager's moveItemAtPath:aFile toPath:theRenamedFile |error|:(reference))
	end if
end repeat
1 Like

It works as aspected and is just as fast as the appending script. And yes the filenames (and files) need to be overwritten.

Many thanks for this @peavine

Just a small recap to make this post a bit more clearer. First of all thanks to everybody who helped on this.

I needed a script that adds or removes (seperate scripts) that would add or delete a given text string (query) add the end of the filename and before the file extension (and period). The original script wasnā€™t able to handle multiple periods in the filename (and turned out to be also slow on a large amount of files). This is now fixed with 2 seperate scripts made by @peavine , see below:

Add text string

use framework "Foundation"
use scripting additions

tell application "Finder" to set fileList to selection as alias list
set theText to " {query}" --delete space at beginning if desired
set fileManager to current application's NSFileManager's defaultManager()
repeat with aFile in fileList
	set aFile to (current application's NSString's stringWithString:(POSIX path of aFile))
	set fileExtension to aFile's pathExtension()
	set theRenamedFile to ((aFile's stringByDeletingPathExtension()'s stringByAppendingString:theText)'s stringByAppendingPathExtension:fileExtension)
	set {theResult, theError} to (fileManager's moveItemAtPath:aFile toPath:theRenamedFile |error|:(reference))
end repeat

Delete text string

use framework "Foundation"
use scripting additions

tell application "Finder" to set fileList to selection as alias list
set theText to " {query}" --delete space at beginning if desired
set fileManager to current application's NSFileManager's defaultManager()
repeat with aFile in fileList
	set aFile to (current application's NSString's stringWithString:(POSIX path of aFile))
	set fileExtension to aFile's pathExtension()
	set fileNoExtension to aFile's stringByDeletingPathExtension()
	if (fileNoExtension's hasSuffix:theText) as boolean is true then
		set fileNoExtension to (fileNoExtension's stringByReplacingOccurrencesOfString:theText withString:"")
		set theRenamedFile to (fileNoExtension's stringByAppendingPathExtension:fileExtension)
		set {theResult, theError} to (fileManager's moveItemAtPath:aFile toPath:theRenamedFile |error|:(reference))
	end if
end repeat

I use these in an alfred workflow, see screenshot.

Many thanks to the community!!