Find and replace multiple characters

I need a script that would edit the names of files and folders in a chosen directory. The following characters / : * ? " < > needs to be replaced by an underscore.

I found this script, wich works great but only allows to change a single character into an underscore.


on change_item_names(source_folder, search_parameter, search_string, replacement_string)
	tell application "Finder"
		try
			-- Get the names to be changed 
			if search_parameter contains "Folder" then
				set target_names to (name of folders of source_folder whose name contains search_string) as list
			else if search_parameter contains "File" then
				set target_names to (name of files of source_folder whose name contains search_string) as list
			else
				set target_names to (name of items of source_folder whose name contains search_string) as list
			end if
			
			-- Change the names of the items with those names 
			set astid to AppleScript's text item delimiters
			repeat with i from 1 to (count target_names)
				set this_name to item i of target_names
				set AppleScript's text item delimiters to search_string
				set text_items to this_name's text items
				set AppleScript's text item delimiters to replacement_string
				set name of item this_name of source_folder to text_items as string
			end repeat
			set AppleScript's text item delimiters to astid
		end try
		
		-- Recurse through the subfolders 
		set the_folders to source_folder's folders
		repeat with this_folder in the_folders
			my change_item_names(this_folder, search_parameter, search_string, replacement_string)
		end repeat
	end tell
end change_item_names

change_item_names(choose folder, "Both", "/", " _")

What are the changes I would need to make so the script be able to replace more than one character ?

Can anyone help me on this one? :?

Thank you in advance

You can try this:

Jon

The simplest way would be to call the handler several times, each with a different search string:

set the_folder to choose folder
repeat with this_string in {"/", "\", ":", "*", "?", """, "<", ">"}
  change_item_names(the_folder, "Both", contents of this_string, "_")
end repeat

However, a purpose-built, multi-search handler does seem to work much faster:

on change_item_names(source_folder, search_parameter, search_strings, replacement_strings)
  set ss_count to (count search_strings)
  if ss_count is not (count replacement_strings) then
    error "change_item_names(): the number of search strings does not match the number of replacement strings."
  end if
  tell application "Finder"
    -- Get the names to be changed
    set target_names to {}
    repeat with i from 1 to ss_count
      set search_string to item i of search_strings
      try
        if search_parameter contains "Folder" then
          get (name of folders of source_folder whose name contains search_string and name is not in target_names)
        else if search_parameter contains "File" then
          get (name of files of source_folder whose name contains search_string and name is not in target_names)
        else
          get (name of items of source_folder whose name contains search_string and name is not in target_names)
        end if
        set target_names to target_names & result
      on error
      end try
    end repeat
    
    -- Change the names of the items with those names
    if target_names is not {} then
      set astid to AppleScript's text item delimiters
      set AppleScript's text item delimiters to ASCII character 1
      set work_string to target_names as string
      repeat with i from 1 to ss_count
        set AppleScript's text item delimiters to item i of search_strings
        set text_items to work_string's text items
        set AppleScript's text item delimiters to item i of replacement_strings
        set work_string to text_items as string
      end repeat
      set AppleScript's text item delimiters to ASCII character 1
      set new_names to work_string's text items
      set AppleScript's text item delimiters to astid
      repeat with i from 1 to (count target_names)
        set name of item (item i of target_names) of source_folder to (item i of new_names)
      end repeat
    end if
    
    -- Recurse through the subfolders
    set the_folders to source_folder's folders
    repeat with this_folder in the_folders
      my change_item_names(this_folder, search_parameter, search_strings, replacement_strings)
    end repeat
  end tell
end change_item_names

set search_strings to {"/", "\", ":", "*", "?", """, "<", ">"} -- ":" is unlikely
set replacement_strings to {"_", "_", "_", "_", "_", "_", "_", "_"}

change_item_names(choose folder, "Both", search_strings, replacement_strings)

This is a droplet I wrote to experiment with removing or replacing characters. It asks for the character to be removed, and then for the one to replace it with, so it assumes you want to remove the same character from all the files dropped.


on open theFiles
	set defDel to AppleScript's text item delimiters
	set badCharacter to display dialog "Please enter the character you wish to remove:" default answer "\" with icon note -- "\" is actually ""
	set newCharacter to display dialog "Please enter the new character. If none, leave blank." default answer "" with icon note
	repeat with x from 1 to (count theFiles)
		set firstItem to item x of theFiles
		tell application "Finder"
			set oldName to name of firstItem
			set AppleScript's text item delimiters to text returned of badCharacter
			set newNameList to text items of oldName
			set AppleScript's text item delimiters to text returned of newCharacter
			set newName to text items of newNameList as string
			set name of firstItem to newName
		end tell
	end repeat
	set AppleScript's text item delimiters to defDel
end open


Sorry. Too much legacy thinking in my previous post. This is much better:

on change_item_names(source_folder, search_parameter, search_strings, replacement_strings)
  set ss_count to (count search_strings)
  if ss_count is not (count replacement_strings) then
    error "change_item_names(): search/replace string count mismatch."
  end if
  
  tell application "Finder"
    -- Get the names to be changed
    try
      if search_parameter contains "Folder" then
        set source_names to (name of folders of source_folder)
      else if search_parameter contains "File" then
        set source_names to (name of files of source_folder)
      else
        set source_names to (name of items of source_folder)
      end if
      
      -- Change the names of the items with those names
      set astid to AppleScript's text item delimiters
      set AppleScript's text item delimiters to ASCII character 1
      set work_string to source_names as string
      repeat with i from 1 to ss_count
        set AppleScript's text item delimiters to item i of search_strings
        set text_items to work_string's text items
        set AppleScript's text item delimiters to item i of replacement_strings
        set work_string to text_items as string
      end repeat
      set AppleScript's text item delimiters to ASCII character 1
      set new_names to work_string's text items
      set AppleScript's text item delimiters to astid
      repeat with i from 1 to (count source_names)
        if item i of source_names is not item i of new_names then
          set name of item (item i of source_names) of source_folder to (item i of new_names)
        end if
      end repeat
    on error
    end try
    
    -- Recurse through the subfolders
    set the_folders to source_folder's folders
    repeat with this_folder in the_folders
      my change_item_names(this_folder, search_parameter, search_strings, replacement_strings)
    end repeat
  end tell
end change_item_names

set search_strings to {"/", "\", ":", "*", "?", """, "<", ">"} -- ":" is unlikely
set replacement_strings to {"_", "_", "_", "_", "_", "_", "_", "_"}

change_item_names(choose folder, "Both", search_strings, replacement_strings)

Here’s a shorter but non-vanilla solution using Acme’s Replace command. You can replace every character in one pass.

--define the characters to be removed
property nukeChar : {"/", "\", ":", "*", "?", """, "<", ">"}
--define the path to a folder to move duplicate files to(you could skip this and choose a new name for the file instead)
property dupFolder : "Macintosh HD:Desktop Folder:Duplicates:"

set myFolder to choose folder --choose a folder with items to be renamed
--get the name of every item of that folder
set folderItems to list folder myFolder without invisibles 
repeat with thisItem in folderItems --repeat with every item
--splice the folder path to the file name to get the full alias path
	set thisPath to (myFolder as string) & thisItem as string 
--replace the unwanted characters in the name
	set newFileName to ACME replace nukeChar in thisItem with "_" 
	try --try because the name may already exist
		tell application "Finder" to set the name of alias thisPath to newFileName
	on error
		--do something if the file already exists (?move the file, or change the name)
		tell application "Finder" to move file thisPath to folder dupFolder with replacing
	end try
end repeat

Best,

{Sigh.} Hello again. Sorry to outstay my welcome. :wink: Here’s a minor bug fix and some improved comments for my vanilla effort. No more after this, I promise. :slight_smile:

on change_item_names(source_folder, search_parameter, search_strings, replacement_strings)
  set ss_count to (count search_strings)
  if ss_count is not (count replacement_strings) then
    error "change_item_names(): search/replace string count mismatch."
  end if
  
  tell application "Finder"
    -- Get the names of all the items of the required type in the folder
    try
      if search_parameter contains "Folder" then
        set source_names to (name of folders of source_folder) as list
      else if search_parameter contains "File" then
        set source_names to (name of files of source_folder) as list
      else
        set source_names to (name of items of source_folder) as list
      end if
      
      -- Combine the found names into one, (ASCII character 1)-delimited string for convenience
      set astid to AppleScript's text item delimiters
      set AppleScript's text item delimiters to ASCII character 1
      set work_string to source_names as string
      
      -- Perform any substitutions within the string
      repeat with i from 1 to ss_count
        set AppleScript's text item delimiters to item i of search_strings
        set text_items to work_string's text items
        set AppleScript's text item delimiters to item i of replacement_strings
        set work_string to text_items as string
      end repeat
      
      -- Uncombine the treated names
      set AppleScript's text item delimiters to ASCII character 1
      set new_names to work_string's text items
      set AppleScript's text item delimiters to astid
      
      -- Give any names that have changed to the appropriate items
      repeat with i from 1 to (count source_names)
        if item i of source_names is not item i of new_names then
          set name of item (item i of source_names) of source_folder to (item i of new_names)
        end if
      end repeat
    on error
    end try
    
    -- Recurse through the subfolders
    set the_folders to source_folder's folders
    repeat with this_folder in the_folders
      my change_item_names(this_folder, search_parameter, search_strings, replacement_strings)
    end repeat
  end tell
end change_item_names

set search_strings to {"/", "\", ":", "*", "?", """, "<", ">"} -- ":" is unlikely
set replacement_strings to {"_", "_", "_", "_", "_", "_", "_", "_"}

change_item_names(choose folder, "Both", search_strings, replacement_strings)

A big thank-you to all. Your help on this little matter as been greatly appreciated. That last one just works like a charm :slight_smile: