Re: perl script vs bash
Re: perl script vs bash
- Subject: Re: perl script vs bash
- From: Ron Hunsinger <email@hidden>
- Date: Fri, 25 Aug 2006 18:41:00 -0700
At 7:09 PM +0200 8/25/06, Sylvain Pascal wrote:
and mine is:
#!/usr/bin/perl -w
use File::Basename;
foreach (@ARGV){
$file=basename $_;
$dir=dirname $_;
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";
}
Whereas the first script works well on Automator, the second doesn't.
I did this workflow:
get selected files > execute a script perl
thanks of your attention,
You can often find out why a script fails by
looking at Automator's log. (In Automator, choose
the menu command View->Show Log.)
In your case, you have two problems. The first,
more serious issue is that you aren't properly
quoting the arguments you pass to system().
Consider the possibility that your filename is
something like "Important stuff", with a space in
it. If you construct the command with
system ("tar czf $file.tgz $file")
that expands to
system ("tar czf Important stuff.tgz Important stuff")
and tar thinks you want to create an archive
named "Important" containing a file named
"stuff.tgz". The other two parameters are ignored.
In the bash script, notice the quotes in "$f".
They tell bash that the value of $f is to taken
as a single parameter, even if it contains
characters the might otherwise split it into
multiple parameters. You need to to do the
equivalent in your perl script.
You might naively try adding the quote characters yourself, as in
system ("tar czf \"$file.tgz\" \"$file\"")
but that won't work. (Hint: what if the filename
contains a quote character? What if it contains a
backslash?)
The easiest way to do quoting correctly is to not
do it at all! Do not attempt to build a (properly
quoted) shell command and then pass that command
to the shell hoping it will unquote the command
the way you want. After all, your perl script is
not a human typing into a command line window;
your script is talking directly to the system.
Instead of saying "here is some text that a human
might have typed in, you figure out what the
parameters are", come directly to the point. Say
"here's the command name, here's the first
parameter, here's the second parameter, ...).
In perl, you do that by passing more than one
argument to the system() function. Write the call
as:
system ("tar", "czf", "$file.tgz", $file)
and bypass the command-line processing
completely. The shell doesn't have to guess where
one parameter ends and the next begins. In fact,
the shell doesn't get involved at all. Your
script invokes tar directly, just like a shell
would after it figured out what the parameters
were.
The second problem is less serious, but it's
probably the one you're tripping over. The bash
and perl scripts you've shown behave slightly
differently when one of the selected items is a
folder.
In either case, the string that automator passes
to a script for a directory name includes the
trailing slash. For example,
/Users/joe/Documents/Important Stuff/Recent Files/
The unix commands know to drop the trailing slash
first, so they break that down as
dirname -> '/Users/joe/Documents/Important Stuff'
basename -> 'Recent Files
But Perl's File::Basename module doesn't do that.
With the same full pathname, you get
dirname -> '/Users/joe/Documents/Important Stuff'
basename -> ''
and
fileparse -> ('', '/Users/joe/Documents/Important Stuff/Recent Files/')
This is probably a bug.
It's the passing of an empty basename to tar that
causes the error. In your original version
(without proper quoting), tar just sees that you
want to create and archive, but didn't say what
to put into it. With proper quoting, the error
message changes, to say that the filename is
empty, but it's still an error.
One workaround is to detect and remove the
trailing slash. If you do that, you're back in
the land of OS-dependent filename parsing, and
you've lost any advantage File::Basename might
have. You might as well just use normal pattern
matching:
#!/usr/bin/perl
use strict;
use warnings;
foreach (@ARGV) {
die "Where would you put it?\n" if $_ eq '/';
my ($dir, $file) = m(^(.*/)([^/]+)/?$) or die "Cannot parse filename $_\n";
die "Where would you put it?\n" if $dir eq '/Volumes/';
next if $file =~ /.tgz$/; # don't zip a zip
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 $file: $!\n"; }
There still remains the problem that 'die' isn't
doing anything useful from within Automator. For
debugging, I changed 'die' to 'print' everywhere,
and added a View Results step to see the output.
That's obviously not satisfactory. (For one
thing, it makes Automator say the script ran
successfully.)
I've added tests for obviously bad selections.
You can probably think of some more. (I assume
tar will catch a non-writable output directory.)
_______________________________________________
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