Re: "Get Selected Finder Items" and "Get Specified Finder Items"
Re: "Get Selected Finder Items" and "Get Specified Finder Items"
- Subject: Re: "Get Selected Finder Items" and "Get Specified Finder Items"
- From: Ron Hunsinger <email@hidden>
- Date: Wed, 30 Aug 2006 15:16:28 -0700
Title: Re: "Get Selected Finder Items" and
"Get Specified Finder
At 10:42 AM +0200 8/29/06, Sylvain Pascal wrote:
As i'm a french, i got a french
translated Automator, so excuse me if you didn't understand my english
translation...
It wasn't that I didn't understand the translation. It was that
there wasn't enough detail. You said there were two actions that were
the same but behaved differently. They aren't the same, not even in
French. One is "Obtenir les éléments du
Finder indiqués" and the other is "Obtenir les éléments Finder
sélectionnés". And you didn't say how they behaved
differently, beyond that one of them "made the process go wrong"
and the other didn't.
(Although, looking at Automator in French, I can see your
problem. The list of Actions is too narrow to show the full names, and
they both get shortened to "Obtenir les
élément...". It's only if you click on them and look at
the summary that you see that the full names are indeed different. But
you have to pay close attention, because you can't see both full names
at the same time. In English, they get shortened to "Get
Specified Finder..." and "Get Selected Finder I...",
which are easily seen to be different.)
So, as i told you before, i tried to
understand what the difference is between the results from "Get
Selected Finder Items" and those from "Get Specified Finder
Items". And, after a View Result action of each result, i got
this:
"Get Specified
Finder Items" ==> {alias "Macintosh
HD:Users:sylvainpascal:Desktop:inscription_université:"}
and :
"Get Selected
Finder Items"==> {folder "inscription_université"
of folder "Desktop" of folder "sylvainpascal" of
folder "Users" of startup disk of application
"Finder"}
Although i saw that before running the
script action, Automator runs a specific process to obtain the right
conversion:
the "get selected Finder
Items":
Data conversion from
("com.apple.finder.file-or-folder-object") to
("com.apple.cocoa.string")
error followed in the perl
script:
"an error tar: Substituting `.' for
empty member name
the "Get Specified Finder
Items":
Data conversion from
("com.apple.applescript.alias-object") to
("com.apple.cocoa.string")
all works well in the Perl
Script!
Now that's the level of detail that I needed. Now it all makes
sense.
"Get Specified Finder Items" stores within itself a
list of Finder items (files, folders, disk volumes) that you built
when you created the script. The idea is to create a workflow that
will operate on the same items, every time it's run. It makes sense
that they would store that list is as a list of aliases, with the
advantage that the workflow will continue to operate on those items,
even if they're moved or renamed. So the natural thing to pass to the
next step is a list of com.apple.applescript.alias-object.
A "Get Selected Finder Items" step, on the other hand,
stores nothing within itself. It asks Finder for the current
selection, each and every time it's run. What it passes to the next
step in the workflow is what it got from Finder, namely a list of
com.apple.finder.file-or-folder-object.
Either way, though, the following "Run Shell Script"
needs a list of strings that it can pass to the script either as
command-line arguments or as standard input. Whatever it gets from the
previous step has to be coerced to (a list of) com.apple.cocoa.string,
as you show.
And this additional piece of information, namely that different
conversions are being used, points us to the desired explanation. The
different conversions are handling folders differently!
The conversion from com.apple.finder.file-or-folder-object DOES
APPEND a trailing slash if the item referred to is a folder.
The conversion from com.apple.applescript.alias-object DOES NOT
APPEND a trailing slash, even if the alias points to a folder.
That trailing slash is what's causing the error in the next step.
Unix convention is that the trailing slash on the name of a directory
is optional. Unix commands (including shell scripts) should do the
same thing whether the trailing slash is present or not.
You can verify this in the shell. Open a terminal window, and
enter (or copy and paste) these commands at the prompt:
dirname /a/b/c
dirname /a/b/c/
basename /a/b/c
basename /a/b/c/
You'll see that dirname returns the same value (/a/b) whether the
trailing slash is present or not, and basename likewise returns the
same value (c) either way.
But the Perl module "File::Basename" is broken. Its
basename() function fails to ignore a trailing slash. You can see this
for yourself in the shell by entering the lines:
perl -e 'use File::Basename; print
dirname("/a/b/c" ),"\n";'
perl -e 'use File::Basename; print
dirname("/a/b/c/"),"\n";'
perl -e 'use File::Basename; print
basename("/a/b/c" ),"\n";'
perl -e 'use File::Basename; print
basename("/a/b/c/"),"\n";'
As you can see, the first three lines agree with the output from
the command-line tools, but the fourth line incorrectly returns an
empty string.
And that's the sequence that leads to your error:
"Get Selected Finder Items" returns a list of
com.apple.finder.file-or-folder-object. The conversion from that to
com.apple.cocoa.string (which is what "Run Shell Script"
needs) appends a slash to the end of the name if the finder item is a
folder. Your script doesn't handle that trailing slash gracefully,
because it calls the function basename(), which doesn't. Specifically,
the trailing slash cause basename() to return an empty string. When
that string reaches tar, it produces the error "tar: Substituting
`.' for empty member name".
BTW: That error from tar is not fatal. The script ran as if you
had typed
cd /Users/sylvainpascal/Desktop/
tar czf .tgz .
which would have tried to compress your Desktop folder into a(n
invisible) file named .tgz on your desktop. You might want to clean
that up. (I found a slew of .tgz files on my computer after playing
with this. I think I got them all.)
the Script perl (corrected thanks to you)
is:
use
File::Basename;
print
"@ARGV";
foreach
(@ARGV){
print "$_
\n";
$file=basename
$_;
$dir=dirname
$_;
print "$dir
--> $file \n";
chdir $dir or die
"erreur, impossible de changer de répertoire:
$!";
!system
("tar", "czf", "$file.tgz", $file) or
die "erreur à l'éxecution de la commande tar -czf:
$!\n";
}
The fact that it's the trailing slash that causes the problem is
something I told you before. I also told you before that the fix was
to not rely on File::Basename. I see you ignored that advice. To
repeat, instead of the lines:
use File::Basename;
.
.
.
$file=basename $_;
$dir=dirname $_;
you should have:
use strict;
use warnings;
.
.
.
my ($dir,$file) = m(^(.*/)([^/]+)/?$) or die
"...";
Get in the habit of using both strict and warnings in every Perl
script you write. You'll get more error messages, but your scripts
will be much easier to debug.
Some comments on that last line:
m(...) is the pattern match operator. Usually
that's written /.../,
but then any / in the pattern needs to be
escaped, as \/. I find the
'slanted toothpicks' harder to read.
Compare
m(^(.*/)([^/]+)/?$)
/^(.*\/)([^\/]+)\/?$/
which both mean exactly the same thing.
The pattern has the form m(^...$), so it
matches the entire string.
The /? at the end matches the optional
trailing slash.
The parentheses in 'my ($dir, $file)' put the
pattern match into
list context. That makes it return a list of
the things matched by
parenthesized subpatterns. In
particular,
$dir is set to what's
matched by (.*/), that is, the longest
prefix that ends with a slash and lets the rest of pattern match
$file is set to what's
matched by ([^/]+). That is, it can't be
the empty string (because of the +) and can't contain a slash.
It won't match the entire file system. If the
full path is "/", you'll
die, because there's no non-empty string for
$file to match. This is
good. If you tried to zip up everything, where
would you put it? You
can't put it inside what you're trying to
zip.
It won't detect that you're trying to compress
a non-boot volume.
For example, if you had a disk called
"OtherDisk", the path presented
to your script would be
"/Volumes/OtherDisk/" (with or without the
trailing slash). It will split that into
"/Volumes/" and "OtherDisk",
and tar will happily create the file
/Volumes/OtherDisk.tgz! Note that
/Volumes is world-writable, so even a
non-admin user can put files there.
/Volumes is also invisible, so you won't know
where all that disk
space went. Your script should probably guard
against this.
It will match files and directories at the
root level. For example,
"/Documents" (with or without a
trailing slash) will correctly split
into "/" and "Documents",
and you'll create /Documents.tgz.
It differs from File::Basename->dirname()
in that $dir will wind up with
a trailing slash in all cases. That's OK. The
only thing you do with
$dir is pass it to chdir(), which handles
trailing slashes correctly.
You don't want to just ignore the problem, even though you now
have a workflow that works. For one thing, you want to build up a
library of useful workflow actions that you can use in other
workflows. If you try to use this Perl script, either as-is or as a
pattern for a similar action in another workflow, it may be that it
will be getting its input in that workflow from a step that produces
com.apple.finder.file-or-folder-object, and you'll have to solve the
problem anyway. Better to solve it now.
For another thing, it's probably a bug that the two conversion
routines produce different strings for the same folder. Apple may at
some time remedy this by changing one of them to agree with the other.
It's hard to say which way they'll go, but I think it more likely that
they'll make both conversions append the slash. In that case, your
script will break, and you won't know why. Again, it's better to
address the problem now by making your script ignore a trailing
slash.
And finally, "Get Specified Finder Items" ("Obtenir les éléments du Finder
indiqués") and "Get Selected Finder Items"
("Obtenir les éléments Finder
sélectionnés") truly do different things. The first one
always returns the same list; the second returns a list that varies
from run to run. I don't know which behavior you want, but you should
choose the one that does what you want, not the one that makes your
Perl script work. Your script should work with either one.
So i said that the "Get selected
Finder Items" makes the process go wrong whereas the "Get
specified Finder Items" (if we don't select any files by default)
gives what we want (the path and the file name).
That's my process:
"Get Specified Finder Items"
-> Script Perl
That comment puzzled me, because it's not what should happen. If
you "Get Specified Finder Items" but don't specify any
files, you should get exactly what you ask for: nothing.
So I played around with that, thinking that maybe "Get
Specified Finder Items" had an undocumented feature where it
would "Get Selected Finder Items" if the list was empty, or
something like that.
And then I noticed that it accepts an input. What it does is, it
ADDS the specified Finder items to the list that comes into it. And,
as a side effect, it converts them to aliases, to agree with the
format of its normal output.
So I'm guessing that your process REALLY is:
"Get Selected Finder Items"
-> "Get Specified
Finder Items"
-> "Run Shell Script".
That is, you're using both! You use "Get Selected Finder
Items" to get the current selection, as a list of
com.apple.finder.file-or-folder-object. You then pass that through a
"Get Specified Finder Items", with an empty list so that it
adds nothing new, but has the side effect of converting everything in
the list into a com.apple.applescript.alias-object. Then you feed that
to the "Run Shell Script" action, which starts off by
converting each item to a com.apple.cocoa.string.
In short, you're relying on the fact that the indirect
conversion
com.apple.finder.file-or-folder-object
->
com.apple.applescript.alias-object
-> com.apple.cocoa.string
produces a different result than the direct conversion
com.apple.finder.file-or-folder-object
-> com.apple.cocoa.string
Interesting! I'd never have suspected that if you hadn't provided
the extra detail about what was happening. I learn something new every
day.
But you should still just fix the script. Then you won't need the
extra step, and you'll be safe if Apple ever changes the alias-object
-> string conversion to add a slash for a directory.
-Ron Hunsinger
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Automator-users mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden