set x to (choose folder)
tell application "Finder" to set oldNames to name of every file of x
repeat with theName in oldNames
set z to (do shell script "echo " & quoted form of theName & " | sed -E '
# Replace all non-alphanumeric character groups with single spaces.
s/[^[:alnum:]]+/ /g
# Replace the last space with a full stop.
s/ ([[:alnum:]]+)$/.\\1/
# Convert the entire name to lower case.
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
# Store a copy in the hold space.
h
# Remove the first space and everything after it from the original.
s/ .+//
# Convert what's left (first three characters) to upper case.
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
# Append (a linefeed and) the stored copy to the result.
G
# Replace the linefeed and everything up to and including the copy's first space with a dash.
s/\\n[[:alnum:]]+ /-/'")
tell application "Finder" to set name of file theName of x to z
end repeat
Life is really too easy for English users.
Nobody think to poor citizen whose language require lots of Unicode characters.
If my memory is right, SED ignores all about these characters.
It’s time to teach Unicode to Unix
Yvan KOENIG (VALLAURIS, France) lundi 13 octobre 2014 17:42:15
Well, at least we do have tools like Satimage.osax, and TextWrangler available for free. So, it is best to be a non English unix user on a Mac!
I mostly use a-z, and doesn’t use my special characters that much, but I wonder how emacs, and vi treats unicode characters, maybe those treats the code points correctly in regexps, so that one can use unicode in shell scripts as well.
Edit
The text suite that DJ Bazzie Wazzie wrote as a demo for creating library scripts with dictionaries, in the Unscripted section works with Unicode.
I am not saying that those is a replacement for sed. I am also not saying that it is particularily hard to create a sed version that handles unicode, because the source of sed is public, and the necessary unicode libraries are present on Mac OS x, and so is sourcecode for creating a unicode grep. But it is still unpaid work of course.
set x to (choose folder)
tell application "Finder" to set oldNames to name of every file of x
repeat with theName in oldNames
set z to (change {"[^[:alnum:]]+", "^([[:alnum:]]+) (.+) ([[:alnum:]]+)$"} into {" ", "\\u\\1-\\l\\2.\\l\\3"} in theName with regexp) -- Requires the Satimage OSAX.
tell application "Finder" to set name of file theName of x to z
end repeat
Building on what Nigel did but removing the shell scripting from the rename loop makes this a bit faster.
I would have used the shell instead of the Finder to do the renaming, but I don’t have the time or the oomph at the moment.
There is rudimentary collision-management.
The script renames 58 test files on my system in ~ 0.5 seconds.
Note: I don’t recommend using AppleScript for elaborate mass file-renames, because it’s an operation fraught with possible problems. I own ‘A Better Finder Rename’ and ‘Name Mangler’ (amongst other utilities that can batch-rename), and I usually use one or the other of them for genuinely complex jobs. Preview lets me see what I’m doing and catch collisions or other problems before they happen.
That said - long ago I wrote my own AppleScript rename routine using the Satimage.osax for regex and Keyboard Maestro for a 2-field find/replace dialog (and to run the script). I use it all the time for relatively simple renaming.
set targetFolderPath to ((path to home folder as text) & "test_directory:Nigel_Garvey_Test:")
set posixPathtoTarget to quoted form of (POSIX path of targetFolderPath)
set shCMD to "fileList=$(ls -1 " & posixPathtoTarget & ");
outputNames=$(sed -E '/^[[:alnum:]]+/{
# Replace all non-alphanumeric character groups with single spaces.
s/[^[:alnum:]]+/ /g
# Replace the last space with a full stop.
s/ ([[:alnum:]]+)$/.\\1/
# Convert the entire name to lower case.
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
# Store a copy in the hold space.
h
# Remove the first space and everything after it from the original.
s/ .+//
# Convert what's left (first three characters) to upper case.
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
# Append (a linefeed and) the stored copy to the result.
G
# Replace the linefeed and everything up to and including the copy's first space with a dash.
s/\\n[[:alnum:]]+ /-/
}' <<< \"$fileList\";);
echo \"$fileList%%%%%$outputNames\";
"
set {oldTIDS, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "%%%%%"}
set {oldNameList, newNameList} to text items of (do shell script shCMD)
set AppleScript's text item delimiters to return
set oldNameList to paragraphs of oldNameList
set newNameList to paragraphs of newNameList
set AppleScript's text item delimiters to oldTIDS
tell application "Finder"
repeat with i from 1 to length of oldNameList
set _file to (targetFolderPath & item i of oldNameList) as alias
try
(targetFolderPath & item i of newNameList) as alias
set name of _file to "temp.temp"
set name of ((targetFolderPath & "temp.temp") as alias) to ("COPY " & item i of newNameList)
on error
try
set name of _file to (item i of newNameList)
on error
set name of _file to "temp.temp"
set name of ((targetFolderPath & "temp.temp") as alias) to ("COPY " & item i of newNameList)
end try
end try
end repeat
end tell
Nigel. One thing you may not know is that the Satimage.osax can find/replace within lists, so you can do your text-massage in one-fell-swoop and make things quite a lot more efficient.
The commented-out line is a demo of a simple error-check, although I have once again implemented rudimentary collision-control later.
set srcFldrPath to ((path to home folder as text) & "test_directory:Nigel_Garvey_Test:")
tell application "Finder" to set oldNameList to name of every file of folder srcFldrPath
set newNameList to (change {"[^[:alnum:]]+", "^([[:alnum:]]+) (.+) ([[:alnum:]]+)$"} into {" ", "\\u\\1-\\l\\2.\\l\\3"} in oldNameList with regexp)
set oldNameList to change "^" into srcFldrPath in oldNameList with regexp
set AppleScript's text item delimiters to "."
# if length of (sortlist newNameList with remove duplicates) ≠length of newNameList then error "Duplicate filenames found!"
tell application "Finder"
repeat with i from 1 to length of oldNameList
set _file to (item i of oldNameList) as alias
set _name to item i of newNameList
try
set name of _file to _name
on error e
set name of _file to text item 1 of _name & ".COPY." & text item 2 of _name
end try
end repeat
end tell
The lowercase command also works with lists and is faster in general for mass case conversion. (I haven’t tested against really large texts though.)
Thanks, Chris! That’s handy to know. I knew it worked with multiple lines in a text (that’s how I developed the regex, in fact), but not that it could also handle multiple texts in an AppleScript list ” and multiple lines in multiple texts, it appears. A versatile beast!
First of all, thanks Nigel for your correct script , it works like a charm
I got A new Buzzz…
I need it to ignore files that starts with an “_”
I know the formula of :
“”“”“”"
set x to “abcde"
if first character of x is not "” then
return true
else
return false
end if
“”“”“”"
Witch works well…
But It won’t work when I imply it in this.
set x to (choose folder)
tell application "Finder" to set oldNames to name of every file of x
if first character of oldNames is not "_" then
repeat with thename in oldNames
set z to (do shell script "echo " & quoted form of thename & " | sed -E '
s/[^[:alnum:]]+/ /g
s/ ([[:alnum:]]+)$/.\\1/
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
h
s/ .+//
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
G
s/\\n[[:alnum:]]+ /-/'")
tell application "Finder" to set name of file thename of x to z
end repeat
end if
What am I doing wrong ???
can any one help me please
‘oldNames’ is a list containing the original names of the files. You need to apply the “_” test to the individual names in the list, not to the list itself:
set x to (choose folder)
tell application "Finder" to set oldNames to name of every file of x
repeat with thename in oldNames
if (thename does not start with "_") then
set z to (do shell script "echo " & quoted form of thename & " | sed -E '
s/[^[:alnum:]]+/ /g
s/ ([[:alnum:]]+)$/.\\1/
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
h
s/ .+//
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
G
s/\\n[[:alnum:]]+ /-/'")
tell application "Finder" to set name of file thename of x to z
end if
end repeat
You could instead tell the Finder to ‘set oldNames to name of every file of x whose name does not start with “_”’, but having the script do the filtering as above is faster.
But the script won’t work… It replace with e and a space. for example : Joël => Joe l
But then I try to replace it with X but i saw that is does not even change it too X ?
I don’t get it, it’s like if it skip my line of code…
The tree first SED line are mine that i’m trying to make it work.
Here’s a version that requires Yosemite (or Mavericks with some changes), and uses AppleScriptObjC instead of sed. Cocoa uses ICU regex, which lacks case-conversion operators in replacement templates, so it ends up having to do some extra work. Still, in all but typing time it’s going to be faster than sed.
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
set x to (choose folder)
tell application "Finder" to set oldNames to name of every file of x
repeat with thename in oldNames
if (thename does not start with "_") then
set z to cleanFileName(thename)
tell application "Finder" to set name of file thename of x to z
end if
end repeat
on cleanFileName(thename)
-- make an NSString
set anNSString to current application's NSString's stringWithString:thename
-- fold to lowercase, ASCII-only string; easiest way is to make ASCII data and convert back to NSString
set theData to anNSString's dataUsingEncoding:(current application's NSASCIIStringEncoding) allowLossyConversion:true
set anNSString to (current application's NSString's alloc()'s initWithData:theData encoding:(current application's NSASCIIStringEncoding))'s lowercaseString()
-- replace non alphanumeric characters with single space
set anNSString to anNSString's stringByReplacingOccurrencesOfString:"[^[a-z][A-Z][0-9]\\n\\r]+" withString:" " options:(current application's NSRegularExpressionSearch) range:(current application's NSMakeRange(0, anNSString's |length|()))
-- put in first hyphen and the extension dot, and make it into a mutable string
set anNSString to (anNSString's stringByReplacingOccurrencesOfString:"(?m)^(^[[a-z][A-Z][0-9]]+) (.+) ([[a-z][A-Z][0-9]]+)$" withString:"$1-$2.$3" options:(current application's NSRegularExpressionSearch) range:(current application's NSMakeRange(0, anNSString's |length|())))'s mutableCopy()
-- search for first block of letters and uppercase them
set theNSRegularExpression to current application's NSRegularExpression's regularExpressionWithPattern:"(?m)^(^[[a-z][A-Z][0-9]]+)" options:0 |error|:(missing value)
set findsNSArray to (theNSRegularExpression's matchesInString:anNSString options:0 range:{location:0, |length|:anNSString's |length|()}) as list
repeat with anNSTextCheckingResult in findsNSArray
set theRange to (anNSTextCheckingResult's rangeAtIndex:1)
(anNSString's replaceCharactersInRange:theRange withString:((anNSString's substringWithRange:theRange)'s uppercaseString()))
end repeat
-- coerce back to text
return anNSString as text
end cleanFileName
And, well, just because, here’s a version of the clean-up handler that eschews regex in favor of a text scanner. It probably looks a little more complicated, but in this case it’s faster than regex, at least if you do each name individually.
Sorry – that’s why I said “Here’s a version that requires Yosemite (or Mavericks with some changes)”. Perhaps Nigel or Chris will have non-ASObjC solution.