Changing file name using tell application "Finder"?

Hi there! I’m Jennifer and I’m new. I’ve been a programmer and a mac user both for about four years, but I’m finally just starting to try Applescript, which is awesome, by the way, and I wish I’d started sooner.

I have a real back-to-basics sort of question. I have searched and googled for about 8 work-hours and I’m ready to appeal to real people for help. Even though I have a work-around, I’d really prefer to do it the “right” way.

I’m working on a project where my user will create videos on her MacBook using iMovie, and then when she’s happy with her movie, she’ll drag and drop it into a destination folder which is attached to the Applescript which will launch all sorts of magical actions and publish her video on her website.

The first thing I want it to do is rename the file to a safe filename that’s not going to bork the rest of the processes. Specifically, to replace the spaces and semicolons with hyphens (iMovie, WHY do you put semicolons in the filename?!). I’m doing the regex work in an external perl script, which is going well, but when it comes to doing the actual file rename, it fails.

on adding folder items to this_folder after receiving these_items
	repeat with aFile in these_items
		set cleanFileName to do shell script "perl /Users/*****/newsroom/cleanFileName.pl \"" & aFile & "\";"
		do shell script "echo \"" & aFile & "\" > /Users/*****/Desktop/file;"
		do shell script "echo \"" & cleanFileName & "\" >> /Users/*****/Desktop/file;"
		tell application "Finder"
			try
				set name of aFile to cleanFileName
			on error
				display dialog "couldn't rename file"
			end try
		end tell
	end repeat
end adding folder items to

I’ve tried about 1000 permutations of the line “set name of aFile to cleanFileName” and it always comes back with the error. I’ve tried using move instead of set name, I’ve tried using aliases instead, etc. It just seems like such a basic thing, and I’m feeling pretty foolish that I’m already stumped this early in the process.

The output in the Desktop/file file is:
Macintosh:Users::desktop:ToPublish:test; file.txt
Macintosh:Users:
:desktop:ToPublish:test–file.txt
So I know the perl script is doing its thing and the full paths are being stored in the variables. I can’t imagine what I’m missing. Unless it’s the semicolons and spaces that are messing it up in the first place, in which case I’m in a Catch-22.

Anyway, like I said, I do have a workaround, which is to use mv on do shell script.

on adding folder items to this_folder after receiving these_items
	repeat with aFile in these_items
		set cleanFileName to do shell script "perl /Users/*****/newsroom/cleanFileName.pl \"" & aFile & "\";"
		set dirtyPosix to POSIX path of aFile
		set cleanPosix to POSIX path of cleanFileName
		do shell script "echo \"" & dirtyPosix & "\" > /Users/*****/Desktop/file;"
		do shell script "echo \"" & cleanPosix & "\" >> /Users/*****/Desktop/file;"
		do shell script "mv \"" & dirtyPosix & "\" \"" & cleanPosix & "\";"
	end repeat
end adding folder items to

That does work, so I’m not overly worried, but if I’m really going to get into Applescripting like I’d like to, I figure I should probably start doing things the right way from the start.

Here’s where it gets really weird, though. The Desktop/file output of that second script is:
/Users//Desktop/ToPublish/test–file.txt
/Users/
/Desktop/ToPublish/test–file.txt

but if I comment out the mv line, it is:
/Users//Desktop/ToPublish/test; file.txt
/Users/
/Desktop/ToPublish/test–file.txt

It’s like it’s doing the mv before the do shell script echoes, or some weird aliasing or something. I would think a scripted language would do the lines in order, but anyway, that’s just quirky, it’s not really my question.

Sorry this post has gotten way longer than I intended. I’m really excited about this project and getting into Applescript. I’ll keep updating on my progress if anyone’s interested. Oh, and just to make this post just a little bit longer, here’s the perl code for the cleanFileName.pl script:
#!/usr/bin/perl
@data = @ARGV;
$data = join(“-”,@data);
$data =~ s/;/-/g;
$data =~ s/\s/-/g;
print “$data\n”;

Model: MacBook
AppleScript: 1.10.7
Browser: Safari 525.22
Operating System: Mac OS X (10.4)

This should make it clearer:

tell application "Finder"
	set x to choose file
	set y to name of x
	set y to quoted form of y
	set z to do shell script "echo " & y & " | perl -pe 's/[; ]/-/g'"
	set name of x to z
end tell

I always try to use applescript commands whenever possible. They are almost always faster than “do shell script” commands. As such here’s a find/replace handler you can use. Applescript’s text item delimiters works great for this. The following will find the semi-colons in a string and replace them with a hyphen…

set fileName to "some movie file name;with a semicolon.mov"
set search_string to ";"
set replacement_string to "-"

set newFileName to findReplace(fileName, search_string, replacement_string)

(*============ SUBROUTINES ==============*)
on findReplace(theString, search_string, replacement_string)
	if theString contains search_string then
		set AppleScript's text item delimiters to search_string
		set text_item_list to text items of theString
		set AppleScript's text item delimiters to replacement_string
		set theString to text_item_list as text
		set AppleScript's text item delimiters to ""
	end if
	return theString
end findReplace

Regarding changing a file’s name… Apple already gave you such a script. Apple gives us many example scripts so we can learn applescript. You can learn a lot from looking at their code. You can find them in /Library/Scripts folder. Look in the folder called “Finder Scripts” for the one you want to change a file name.

That works too, but you’ve got to call the subroutine once for each character, so adding in the space would make it look something like this:

set fileName to "some movie file name;with a semicolon.mov"
set search_string to ";"
set replacement_string to "-"

set newFileName to findReplace(fileName, search_string, replacement_string)
set search_string to " "

set newFileName to findReplace(newFileName, search_string, replacement_string)


(*============ SUBROUTINES ==============*)
on findReplace(theString, search_string, replacement_string)
	if theString contains search_string then
		set AppleScript's text item delimiters to search_string
		set text_item_list to text items of theString
		set AppleScript's text item delimiters to replacement_string
		set theString to text_item_list as text
		set AppleScript's text item delimiters to ""
	end if
	return theString
end findReplace

While it may be slightly faster than the do shell script as is, once you add other characters that you want to filter, you’ll probably end up with a loop, and that’s when the speed advantage will flip to the shell script.

There’s a large overhead when you use “do shell script”. As such I ran a speed test on the 2 methods. The test I used is as outlined by Nigel Garvey in post 4 found here: http://bbs.applescript.net/viewtopic.php?id=17015

Here’s the results. 500 tests of each method were conducted for these results.

  1. applescript method finding/replacing both characters with a hyphen took 0.193 seconds for 500 tests
  2. shell script method took 10.506 seconds for 500 tests
    → the applescript method is over 54 times faster.

So you can see the speed advantage I mentioned.

I ran two scripts twice each from a shell using the time shellname.sh command:
script1.sh

#!/bin/bash
/usr/bin/osascript <<EOT
set fileName to "some movie file name;with a semicolon.mov"
set search_string to ";"
set replacement_string to "-"

set newFileName to findReplace(fileName, search_string, replacement_string)
set search_string to " "

set newFileName to findReplace(newFileName, search_string, replacement_string)


(*============ SUBROUTINES ==============*)
on findReplace(theString, search_string, replacement_string)
	if theString contains search_string then
		set AppleScript's text item delimiters to search_string
		set text_item_list to text items of theString
		set AppleScript's text item delimiters to replacement_string
		set theString to text_item_list as text
		set AppleScript's text item delimiters to ""
	end if
	return theString
end findReplace
EOT

Script2.sh

#!/bin/bash
/usr/bin/osascript <<EOT
tell application "Finder"
	set y to "some movie file name;with a semicolon.mov"
	set y to quoted form of y
	set z to do shell script "echo " & y & " | perl -pe 's/[; ]/-/g'"
end tell
EOT

The results were these times:

Script1.sh:

real 0m0.434s
user 0m0.183s
sys 0m0.138s

real 0m0.362s
user 0m0.177s
sys 0m0.128s

Script2.sh

real 0m0.417s
user 0m0.196s
sys 0m0.143s

real 0m0.399s
user 0m0.187s
sys 0m0.130s

Pretty close to identical.

I don’t really want to debate this because it doesn’t really matter. It’s generally known that anything involving the shell requires a speed hit just from the overhead. The speed test method I used is the generally accepted method. You’ll notice it has things such as “warming up the processor” before the testing to make sure the two tests are fair. As such I’m not sure what side-effects your method has. The method I used has been pretty well scrutinized. Plus you need to do lots of tests in order to factor out anomolies in any testing methods… that’s why I did 500 tests each.

So in general I use applescript methods when possible unless the shell script method is much easier. That’s just me though, you’re welcome to use whatever method you wish.

My method is essentially the method demonstrated in post #6 of the above link. I put more faith in it than the Applescript method because the Applescript method requires repeat loops, and those are not only slow, but their times can vary significantly.

Thanks for your suggestions. I’ll give it another try when I get to work tomorrow. I really appreciate the help.

Hi.

The ‘after receiving’ parameter (ie. ‘these_items’) is a list of aliases to the items than have just gone into the folder. As each alias is concatenated into your ‘do shell script’ texts, it’s automatically coerced to an HFS path (with colons), whereas you’ll undoubtedly need a POSIX path in those situations:

repeat with aFile in these_items
	set filePOSIX to quoted form of POSIX path of aFile
	set cleanFileName to do shell script "perl /Users/*****/newsroom/cleanFileName.pl " & filePOSIX
	do shell script "echo " & filePOSIX & " > /Users/*****/Desktop/file"
	-- etc.
	
end repeat

I don’t know what kind of result your Perl script returns. If it returns a full POSIX path, then you’re telling the Finder to set the name of the file to a full POSIX path instead of just a new file name. But you’ll know better than I whether that’s the case.

When you rename a file that’s in a folder watched by an ‘on adding folder items to’ script, the new name causes the script to be retriggered, which can sometimes lead to problems. It’s best to move items to somewhere neutral before renaming them.

With regard to the speed controversy above, ‘do shell script’ has the overhead of having to open a shell, run the script, and close the shell again afterwards. There also seems to be a slight overhead in the time it takes to fetch the shell commands from disk. This is time in which vanilla AppleScript and OSAXen can simply be getting on with the job. However, once they’re going, shell commands are very fast; so, given an appropriate task, they can sometimes wipe the floor with AppleScript or an OSAX. They’re nearly always faster than the Finder. It’s how you combine everything for the job in hand that gives the speed.

If you have more than one ‘do shell script’ in a row, they can usually be combined into one to reduce the shell overhead:

do shell script "some command"
do shell script "some other command"

-- Combined:
do shell script "some command ; some other command" -- Semicolon between the commands.

In the case of Jennifer’s client, the speed advantage to any particular method will be unnoticeable in the time taken for the single, manual drag-drop and for the folder action to react.

Thanks that worked perfectly. My “final” solution is this:

on adding folder items to this_folder after receiving these_items
	repeat with aFile in these_items
		tell application "Finder"
			set x to aFile
			set y to name of x
			set y to quoted form of y
			set z to do shell script "echo " & y & " | perl -pe 's/[; ]/-/g'"
			set name of x to z
		end tell
	end repeat
end adding folder items to

And it is fast, too!

It should be a little faster this way:

on adding folder items to this_folder after receiving these_items
	tell application "Finder"
		repeat with aFile in these_items
			set y to name of aFile
			set y to quoted form of y
			set z to do shell script "echo " & y & " | perl -pe 's/[; ]/-/g'"
			set name of aFile to z
		end repeat
	end tell
end adding folder items to

I haven’t tested it, but I don’t think you need x, and there’s no need to keep the tell “Finder” block inside the repeat loop.

Also, since you’re changing the name of a file in the folder being watched, the script will get called again. This could be a problem if you dump a large number of files into that folder at the same time.

Thanks, that’s some good advice. I don’t think she’ll want to drop more than one file in at a time, but I appreciate learning the best practices.