Replacing invalid characters in the filename with dashes using AppleScript

爱⌒轻易说出口 提交于 2020-06-17 10:05:33

问题


My goal is to create service in Automator using AppleScript or Javascript which replaces all invalid characters of selected filename ()[\\/:"*?<>|]+_ and spaces with dashes (-) and make filename lowercase.


回答1:


The replacement of impermissible characters in a file/folder name can be achieved by utilizing a Bash Shell script in your Automator Service.

The following steps describe how to achieve this:

Configuring Automator

  1. Launch Automator
  2. Type ⌘N, or choose File > New from the Menu bar.
  3. Select Service and click Choose
  4. At the top of the canvas area configure its settings as follows:

  5. Select Library at the top of the panel/column on the left:

    • In the search field type: Get Select Finder items and drag the Get Select Finder items action into the canvas area.

    • In the search field type: Run Shell and drag the Run Shell Script action into the canvas area.

  6. Configure the top part of the Run Shell Script action as follows:

  7. Add the following Bash script to the main area of the Run shell Script action:

    #!/usr/bin/env bash
    
    # The following characters are considered impermissible in a basename:
    #
    #  - Left Square Bracket:   [
    #  - Right Square Bracket:  ]
    #  - Left Parenthesis:      (
    #  - Reverse Solidus:       \
    #  - Colon:                 :
    #  - Quotation Mark         "
    #  - Single Quotation Mark  '
    #  - Asterisk               *
    #  - Question Mark          ?
    #  - Less-than Sign         <
    #  - Greater-than Sign      >
    #  - Vertical Line          |
    #  - Plus Sign              +
    #  - Space Character
    #  - UnderScore             _
    #
    #  1. Sed is utilized for character replacement therefore characters listed
    #     in the bracket expression [...] must be escaped as necessary.
    #  2. Any forward slashes `/` in the basename are substituted by default with
    #     a Colon `:` at the shell level - so it's unnecessary to search for them.
    #
    declare -r IMPERMISSIBLE_CHARS="[][()\\:\"'*?<>|+_ ]"
    declare -r REPLACEMENT_STRING="-"
    
    # Obtain the POSIX path of each selected item in the `Finder`. Input must
    # passed to this script via a preceding `Get Selected Finder Items` action
    # in an Automator Services workflow.
    declare selected_items=("$@")
    
    declare -a sorted_paths
    declare -a numbered_paths
    
    # Prefix the POSIX path depth level to itself to aid sorting.
    for ((i = 0; i < "${#selected_items[@]}"; i++)); do
      numbered_paths+=("$(echo "${selected_items[$i]}" | \
          awk -F "/" '{ print NF-1, $0 }')")
    done
    
    # Sort each POSIX path in an array by descending order of its depth.
    # This ensures deeper paths are renamed before shallower paths.
    IFS=$'\n' read -rd '' -a sorted_paths <<<  \
        "$(printf "%s\\n" "${numbered_paths[@]}" | sort -rn )"
    
    
    # Logic to perform replacement of impermissible characters in a path basename.
    # @param: {Array} - POSIX paths sorted by depth in descending order.
    renameBaseName() {
      local paths=("$@") new_basename new_path
    
      for path in "${paths[@]}"; do
    
        # Remove numerical prefix from each $path.
        path="$(sed -E "s/^[0-9]+ //" <<< "$path")"
    
        # Replaces impermissible characters in path basename
        # and subsitutes uppercase characters with lowercase.
        new_basename="$(sed "s/$IMPERMISSIBLE_CHARS/$REPLACEMENT_STRING/g" <<< \
            "$(basename "${path}")" | tr "[:upper:]" "[:lower:]")"
    
        # Concatenate original dirname and new basename to form new path.
        new_path="$(dirname "${path}")"/"$new_basename"
    
        # Only rename the item when:
        # - New path does not already exist to prevent data loss.
        # - New basename length is less than or equal to 255 characters.
        if ! [ -e "$new_path" ] && [[ ${#new_basename} -le 255 ]]; then
          mv -n "$path" "$new_path"
        fi
      done
    }
    
    renameBaseName "${sorted_paths[@]}"
    
  8. The completed canvas area of your Automator Service/Workflow should now appear something like this:

  9. Type ⌘S, or choose File > Save from the Menu bar. Let's name the file Replace Impermissible Chars.


Running the Service:

  1. In the Finder:

    • Select a single file or folder and then ctrl+click to show the contextual menu
    • Select the Replace Impermissible Chars service in the contextual menu to run it.
  2. Alternatively, in the Finder:

    • Select multiple files and/or folders and then ctrl+click to show the contextual menu
    • Select the Replace Impermissible Chars service in the contextual menu to run it.

Notes:

  1. The Finder will allow the Service to be run on up to 1000 files/folders selected at once.

  2. If this is the first Automator Service you have created you may (if I've remembered correctly!) need to restart your computer for it to become available via the contextual pop-up menu.

  3. The Bash Shell script will not replace impermissible characters in a file/folder name if either of the following two conditions are met:

    • If a file/folder name already exists which matches the name of a resultant new file/folder name. For example: Let's say we have a file named hello-world.txt and in the same folder a file named hello?world.txt. If we run the Service on hello?world.txt, it's name will not be corrected/changed as this would potentially overwrite the hello-world.txt file which already exists.

    • If the resultant file name is >= to 255 characters. This may of course only occur if you were to change the REPLACEMENT_STRING value (in the Bash/Shell script) to more than one character, instead of just a single hyphen -.




回答2:


It's quite easy with help of Regular Expression and the Foundation Framework bridged to AppleScriptObjC.

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

use framework "Foundation"

set fileName to "New(Foo)*aBcd<B|r.ext"

set nsFileName to current application's NSString's stringWithString:fileName
set nsLowerCaseFileName to nsFileName's lowercaseString()
set trimmedFileName to (nsLowerCaseFileName's stringByReplacingOccurrencesOfString:"[()[\\/:\"*?<>|]+_]" withString:"-" options:(current application's NSRegularExpressionSearch) range:{location:0, |length|:nsLowerCaseFileName's |length|()}) as text
display dialog trimmedFileName



回答3:


Two great solutions here. But as every problem usually has many solutions, I offer another:

    property alphabet : "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    --------------------------------------------------------------------------------
    rename from "HELLO World+Foo(\"Bar\")new.ext"
    --------------------------------------------------------------------------------
    ### HANDLERS
    #
    # rename from:
    #   Receives a text string and processes it for invalid characters, which
    #   get replaced with the specified replacement string (default: "-"),
    #   returning the result
    to rename from filename given disallowed:¬
        invalidCharSet as text : "[()[\\/:\"*?<>|]+_] ", replaceWith:¬
        replacementStr as text : "-"
        local filename
        local invalidCharSet, replacementStr

        set my text item delimiters to {replacementStr} & ¬
            the characters of the invalidCharSet

        text items of the filename as text

        makeLowercase(the result)
    end rename


    # makeLowercase():
    #   Receives a text string as input and returns the string formatted as
    #   lowercase text
    to makeLowercase(str as text)
        local str

        set my text item delimiters to ""

        if str = "" then return ""

        set [firstLetter, otherLetters] to [¬
            the first character, ¬
            the rest of the characters] of str


        tell the firstLetter to if ¬
            it is "-" or ¬
            it is not in the alphabet then ¬
            return it & my makeLowercase(the otherLetters)

        considering case
            set x to (offset of the firstLetter in the alphabet) mod 27
        end considering

        return character x of the alphabet & my makeLowercase(the otherLetters)
    end makeLowercase

This code can be used in a Run AppleScript Automator action, placing rename from... inside the on run {input, parameters} handler, and the rest of the code outside it. It can follow an action that supplies it with a list of files in Finder, or it can receive its input directly from the workflow's input if it is run as a Service.

    property alphabet : "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    on run {input, parameters}
        tell application "Finder" to repeat with f in input
            set the name of f to (rename from f)
        end repeat
    end run

    to rename from ...
        .
        .
    end rename

    to makeLowercase(str as text)
        .
        .
    end makeLowercase


来源:https://stackoverflow.com/questions/50134550/replacing-invalid-characters-in-the-filename-with-dashes-using-applescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!