Smart rule that removes underscore in filenames

I regularly receive insurance documents in which every word in the filename is separated by underscores. I know that I can manually remove them using the renaming script, but I wanted to include a renaming regex or script in the smart rule that I already use to process these files.
I tried using a “Scan Name” regex, but I could not make it work.
I tried having the smart rule “execute script” by copying the text of the “Replace Text in Names.scpt” into the smart rule box, but it gave me an error message.
To make it clear, I want to have the script automatically take a file called

ACH_PAYMENT_LETTER_C79009_PONTCHARTRAIN RESTORATION & HOLDING_030723_139240038.pdf

and change it to

ACH PAYMENT LETTER C79009 PONTCHARTRAIN RESTORATION & HOLDING 030723 139240038.pdf

What am I missing?

I’m curious: what RegEx did you try?

PS: This is rather simply done with pure AppleScript.

property illegalCharacters : {":", "/", "\\"}

on performSmartRule(theRecords)
	set removeCharacter to "_" -- What character do you want to remove?
	set replaceWith to " " -- What character do you want to replace it with?
	set od to AppleScript's text item delimiters -- Cache the default text item delimiters
	
	tell application id "DNtp"
		if replaceWith is in illegalCharacters then log message replaceWith info "This is an illegal character and can't be used in filenames."
		repeat with theRecord in theRecords
			set recordName to (name of theRecord)
			if (recordName contains removeCharacter) then -- Does the file need to be acted on?				
				set AppleScript's text item delimiters to removeCharacter -- If so, delimit with the character to remove
				set nameComponents to (text items of recordName) -- Get a list of the parts of the name that were separated, i.e., delimited by the specified character
				set AppleScript's text item delimiters to replaceWith -- Set the delimiter to the replacement character
				set adjustedName to (nameComponents as string) -- Coerce the list of name components back into a string, joining the parts with the current delimiter.
				set name of theRecord to adjustedName -- Rename the file
			end if
		end repeat -- And move on…
		set AppleScript's text item delimiters to od -- At the end, make sure to reset the text item delimiters
	end tell
end performSmartRule

Thanks! That actually does not seem simple to me, but I think I got it.
Using “Scan Name” with a regex, I was experimenting with many permutations at https://regex101.com/, and also using BBEdit; but it generally involved

((\w*)(\_)(\w+))(?R))?

Replace with

\1 \3

I changed the grouping, changed the recursion operator, et cetera. I also tried the inverse, flanking \w with \_ and replacing with \2. I was able to use the recursion symbol to get matches, but I could not get replacement to work.

RegEx is a sledgehammer to your fly of a filename in this case. :slight_smile:

Did you read the comments in the script I posted?

and an Execute Script > JavaScript variant might look like:

performsmartrule = recs =>
    recs.forEach (r => {
        r.name = r.name().split("_").join(" ")
    })
3 Likes

Or one call to `replaceAll(/_/g," ")

1 Like

Or one call to `replaceAll(/_/g," ")

and, of course, String.replaceAll doesn’t actually need a regular expression either:

performsmartrule = recs =>
	recs.forEach (r => {
		r.name = r.name().replaceAll("_", " ")
	})
2 Likes

I did read them. I understand the script, but my efforts to learn Applescript have been pretty frustrating. Scripts for “Hello world” and “What is your favorite color?” work just fine, but not much beyond that.

For those more interested in this “Applescript text item delimiter” magic trick, here is a pretty good explanation:

1 Like

There’s also a replacement method, available to AppleScript, built into the Foundation library.

(Though you have to work with the NSString type, and convert it back to an AppleScript string at the end)

use framework "Foundation"


on allReplaced(needle, replacement, haystack)
    haystack's stringByReplacingOccurrencesOfString:(needle) ¬
        withString:(replacement)
end allReplaced


on run
    tell current application
        set sample to its (NSString's stringWithString:"abc_def_ghi_")
    end tell
    
    
    allReplaced("_", space, sample) as string
    
    -->  "abc def ghi "
end run
2 Likes