Special way to change filenames

Hello Guys,

I’m a Computer Graphic Artist whit a bit of knowledge on Applescript but needs HELP…
I work with people who does not understand the rules about keeping a server in order…

I’m trying to clean many file names in the server in one shot, Long story… but SHORT

Here’s what I have in a folder :

aCt_train.flow“freight.jpd
aCt-TRAIN…flow“-freight.txt
Art.Jugling -RePt_jo(34).low.jpg
ART-“jugling _ rept&jo_34_low.mp4
Art“jugling rept–jo 34_low.jpg
Spo.magic–ball playoff.jpg
sPO_-MAgic BALL-(playoff).txt

Here’s what I need it in the folder :

ACT-train flow freight.jpd
ACT-train flow freight.txt
ART-jugling rept jo 34 low.jpg
ART-jugling rept jo 34 low.mp4
ART-jugling rept jo 34 low.txt
SPO-magic ball playoff.jpg
SPO-magic ball playoff.txt

I’ve managed to remove all the extra spaces

I’ve managed to replace the 4th character for a single simple dash

I’ve managed to remove all the characters ““_-” exept the “()” “.” “&” or any other strange symbols

I can’t…

…get rid of the periods I need to remove from the name file BUT NOT the extention one

When I add the “.” or the “()” or the “&” in my script, it blocks my script
"“error “Finder got an error: The operation can’t be completed because there is already an item with that name.””

…get the 3 first characters to be in CAP. (Uppercase) AND get the rest of it in lower case

Heres what I got for so far…


set x to choose folder
tell application "Finder"
	set y to every file of x as alias list
	repeat with i from 1 to number of items of y
		set thename to name of item i of y
		set theCommand to " | sed 's/[“_-]/\\ /g' | sed 's/  */\\ /g' | sed 's/^\\(.\\{3\\}\\) /\\1-/'"
		set z to do shell script "echo " & thename & theCommand
		set name of item i of x to z
	end repeat
end tell

If any thing to Add or Sub or Change in my script will help please…

Can any one help me please…
Thank you in advance

Hi.

This seems to do the trick:

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 :wink:

Yvan KOENIG (VALLAURIS, France) lundi 13 octobre 2014 17:42:15

Hello Yvan.

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. :slight_smile:

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. :slight_smile:

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. :confused:

OK. :slight_smile: Here’s an alternative using that awfully good French OSAX Satimage:

set theName to "été...gaRÇON-ÉLÈVE,./,CitroËn....txt"
set z to (change {"[^[:alnum:]]+", "^([[:alnum:]]+) (.+) ([[:alnum:]]+)$"} into {" ", "\\u\\1-\\l\\2.\\l\\3"} in theName with regexp)

In context, that would be:

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

Thanks to McUsrII and Nigel !

Yvan KOENIG (VALLAURIS, France) lundi 13 octobre 2014 22:42:51

Hey Joel,

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

For Nigel & Yvan,

As you probably know I’d do this job on my machine with the Satimage.osax, but Perl works.


do shell script "echo \"été...gaRÇON-ÉLÈVE,./,CitroËn....txt\" | perl \"-Mopen qw/:std :utf8/\" -pe '$_=lc'"

I don’t know how to write that in a standard Perl script just yet, so the command-line version will have to do.

If I feel like it later I’ll come back and rewrite the whole script in Perl just for fun.

For Nigel & Yvan,

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.)


set oldNameList to lowercase oldNameList

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! :slight_smile:

Hey Nigel and guys,

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

Hi.

‘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.

Hey Guys :slight_smile:

Thumbs Up, with a big Thanks for the help…

New development,

I need to replace the French Accent [é è ê …] with e

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.

Sample of files input
aCT“élèctîons scôlaïres.JPG
aCT“élèctîons scôlaïres.txt

gives me “output”:
ACT-e le cti ons sco lai res.jpg
ACT-e le cti ons sco lai res.txt

Should give
ACT-elections scolaires.jpg
ACT-elections scolaires.txt


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/[éÉèÈêÊëË]/e/g
s/[áÁà ÀâÂäÄ]/a/g
s/[íÍìÌîÎïÏ]/i/g
# 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 if
end repeat

Hi Joe,

I think accented characters were actually two keystrokes. You might want to use the unicode numbers.

Edited: but the gurus will be here soon. :slight_smile:

gl,
kel

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.

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

cleanFileName("aCt_trân.flow“fréight.jpd")

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()
	-- make a set of chars we're interested in keeping
	set theNSCharacterSet to current application's NSCharacterSet's characterSetWithCharactersInString:"abcdefghijklmnopqrstuvwxyz0123456789"
	-- make a scanner from string
	set theNSScanner to current application's NSScanner's scannerWithString:anNSString
	-- scan the first block of alphanumeric characters, uppercase them, and add a hyphen
	set {theResult, thePart} to theNSScanner's scanCharactersFromSet:theNSCharacterSet intoString:(reference)
	set theFinalString to ((thePart's uppercaseString()) as text) & "-"
	set lastPart to "" -- will store each block as it's scanned
	-- start looping
	repeat
		-- scan past anything not in our set
		set theResult to theNSScanner's scanUpToCharactersFromSet:theNSCharacterSet intoString:(missing value)
		-- if it returns false, the last block of characters we scanned must be the extension, so add dot plus block and exit repeat
		if theResult as boolean = false then
			set theFinalString to theFinalString & "." & lastPart
			exit repeat
		end if
		-- must have found some unwanted text, so add the last block, inserting a space if we're past the initial block
		if theFinalString ends with "-" then
			set theFinalString to theFinalString & lastPart
		else
			set theFinalString to theFinalString & " " & lastPart
		end if
		-- scan the next block of characters and store it
		set {theResult, lastPart} to theNSScanner's scanCharactersFromSet:theNSCharacterSet intoString:(reference)
		if theResult as boolean = false then exit repeat -- shouldn't happen, but just in case
	end repeat
	return theFinalString
end cleanFileName

Sorry Shane, but your script does not function with my mac.
It tells me << Syntax Error Expected end of line, etc. but found “:” >>

Are you running Yosemite or Mavericks?

It’s at work, can’t update :frowning:
OS X Mountain Lion 10.8.5

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.