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?
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
@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.
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
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
@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
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.
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
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.