MacOS – How to install this script as a folder action

applescriptautomationfolder-actionmacos

I'm trying to setup a script as a folder action that will identify and change unwanted characters in the name of a file added to a target folder.

The intent is to have this automation run as a folder action that should read the name of an incoming file or new folder, check for spaces and dashes, and replace them with underscores.

Here is what I have so far:

on adding folder items to theFolder after receiving theNewItems
    repeat with i from 1 to count of theNewItems
        set thisItem to item i of theNewItems
        tell application "Finder"
            set FileName to name of thisItem
        end tell
    end repeat
    set AppleScript's text item delimiters to {space, "-"}
    set FileName to the FileName's text items
    set AppleScript's text item delimiters to "_"
    set FileName to the FileName as text
end adding folder items to

How can I install this script as a folder action?

Best Answer

This answer is self-contained as a solution to the question above. However, it also serves as a continuation to the previous question that was closed, so incorporates some of the broader requests that were wanted, such as a confirmation dialog with the option to edit the filename, and the option to ignore the validation and use a filename of the user's choice.

on adding folder items to the |folder| after receiving filelist
    local folder, filelist

    tell application "Finder" to repeat with f in the filelist
        try
            set item f's name to my validate(item f's name)
        on error E
            display notification E with title my name
        end try
    end repeat
end adding folder items to
--------------------------------------------------------------------------------
to validate(filename)
    local filename

    script validation
        property old : filename

        on newFilename for old
            local old, new

            set tids to {¬
                {"_", space, "-"}, ¬
                {"_&_", "&"}, ¬
                {"and", "&"}, ""}
            set new to old

            repeat with tid in tids
                set new to text items of new as text
                set my text item delimiters to contents of tid
            end repeat

            new
        end newFilename

        on nameShowingErrors()
            set tids to {¬
                {" ͎", space}, ¬
                {"-͎", "-", "—"}, ¬
                {"&͎", "&"}, ""}
            set info to old as text

            repeat with tid in tids
                set info to text items of info as text
                set my text item delimiters to contents of tid
            end repeat

            info
        end nameShowingErrors

        to confirmRename()
            local new

            set [q, lf] to [quote, linefeed]

            tell (display dialog ["The following characters were", ¬
                " found in the filename: ⟨space⟩, \"-\" ", ¬
                " ⟨hyphen⟩ \"&\" ⟨ampersand⟩", lf, lf, ¬
                " Filename: ", q, nameShowingErrors(), q, ¬
                lf, lf, "By default, these characters will", ¬
                " be replaced with underscores.", ¬
                " Alternatively, you can make your own edits", ¬
                " below and revalidate the filename again."] ¬
                as text ¬
                with title ("Validate Filename") ¬
                default answer (newFilename for old) ¬
                buttons ["Rename", "Keep This Name"] ¬
                default button 1 ¬
                with icon (path to resource ("Finder.icns") ¬
                in bundle application "Finder")) to if ¬
                "Rename" = its button returned ¬
                then return its text returned

            false
        end confirmRename
    end script

    tell validation
        if filename = (newFilename for filename) then return filename
        set newName to confirmRename()
        if newName = false then return its old
        if newName ≠ (newFilename for newName) ¬
            then return validate(newName)
        newName
    end tell
end validate

While this script works, I should remind you of the comment I left against your original question, which has been reiterated above by another user: this script has the potential to be called twice for a single file because renaming files whilst they are inside the folder being watched triggers the folder action again. This happens when the user elects to edit the suggested filename and provides an invalid name. The script will ask for the new filename to be validated by showing the dialog a second time (so far, this is all normal, and entirely what should happen). However, when the user then selects to disregard the naming criteria and use this name anyway, the file is renamed and the script is triggered again. This brings up the dialog for a third time, which isn't what we want to be happening ideally, but is unavoidable.

(This isn't an issue when files are renamed to a validated filename, as the script—although executed twice regardless—doesn't do anything for filenames that don't require changing.)

For the reason above, I would advise you to consider implementing your folder action slightly differently, and have files moved to a new output folder before they get renamed. This is very easy to do by adding a line before set item f's name to... that looks something like this:

move item f to the outputFolder

where you will have defined outputFolder as a property or a variable at the start of the script that points to the location of where the output folder exists (preferably as an alias), e.g.:

set outputFolder to POSIX file "/Users/%you%/My Watched Folder/Processed Files" as alias

But, for now, I have written the script to do what you wanted. I'm assuming that there aren't going to be many instances where you will want to disregard your own naming criteria, so the "three-dialog" phenomenon may not ever arise, or be little more than irksome when it does.


To install a script as a folder action, I'll direct you to the first part of my previous answer, which I've transferred in below.

Folder Actions

A folder action can be created in one of two ways: via Automator, or by way of a folder action script.

Automator on macOS

For information on how to create a folder action script, which I would say is probably the more suitable solution in this case, you may wish to read the Mac Automation Scripting Guide - Watching Folders, which details how to do this.

The section titled Attaching a Folder Action Script to a Folder is the most pertinent to the task.