问题
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
- Launch Automator
- Type ⌘N, or choose
File
>New
from the Menu bar. - Select
Service
and clickChoose
At the top of the canvas area configure its settings as follows:
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.
Configure the top part of the
Run Shell Script
action as follows: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[@]}"
The completed canvas area of your Automator Service/Workflow should now appear something like this:
Type ⌘S, or choose
File
>Save
from the Menu bar. Let's name the fileReplace Impermissible Chars
.
Running the Service:
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.
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:
The Finder will allow the Service to be run on up to 1000 files/folders selected at once.
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.
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 namedhello?world.txt
. If we run the Service onhello?world.txt
, it's name will not be corrected/changed as this would potentially overwrite thehello-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 theREPLACEMENT_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