Alien has asked for the wisdom of the Perl Monks concerning the following question:

Hi again!
I wanted to make a script that can be used to extract archives , but the problem is at the password protected ones.
I capture the output in a variable , but if the password is wrong , the output still shows on the screen,
Here is the part that gives me headaches :
my $output=`unzip -P $password $zipfile`;
If the password is wrong , the message appears in the screen . I figure it has something to do with stderr , but i'm not sure .

Replies are listed 'Best First'.
Re: Output capturing trouble
by ikegami (Patriarch) on Feb 04, 2007 at 21:22 UTC
    Redirect STDERR to be the same as STDOUT:
    my $output=`unzip -P $password $zipfile 2>&1`;

    The return code ($?) probably indicate whether an error has occured or not.

    Update: I had said $& where I meant to say $?. Fixed. Thanks shmem.

Re: Output capturing trouble
by xdg (Monsignor) on Feb 04, 2007 at 21:28 UTC

    Backticks only capture STDOUT, not STDERR. For that, you can look at various approaches for capturing STDERR in perlfaq8 or use IPC::Open3.

    However, I often recommend IPC::Run3 instead, as it's portable and makes things easy. This will capture both STDOUT and STDERR to the $output variable.

    use IPC::Run3; my $output; run3("unzip -P $password $zipfile", undef, \$output, \$output);

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Output capturing trouble
by sgt (Deacon) on Feb 05, 2007 at 10:17 UTC

    Just a small comment, I usually use backticks when I am interested in the output of a command writing to stdout. If it does not then I think it's more appropriate to use simply system. If you don't want stderr then you can close stderr or redirect it to /dev/null (supposing unixlike here), this would be "2>&-" o "2>/dev/null".

    Supposing a program writes to stderr only error conditions (and it should) seldom there is need to capture both stderr and stdout. I usually do it when there is some problem and I want to grep everything " cmd ... 2>&1 | grep ". Another case is when you want to capture the exit status of an intermediate command in a shell pipeline, while normally you would get the exit status of the last one (modern shell have a "pipefail" option -- and more-- to help that)

    % stephan@ape (/home/stephan/t0) % % echo 'just another perl hacker' > secret.txt % stephan@ape (/home/stephan/t0) % % zip -P sesame safe.zip secret.txt adding: secret.txt (stored 0%) % stephan@ape (/home/stephan/t0) % % perl safe_unzip.px sesame safe.zip .cmd: [unzip -qoP sesame safe.zip 2>/dev/null] .okay: got secret file! status = [0] 1 just another perl hacker$
    #!/usr/bin/perl use strict; use warnings; my $password = shift or die "args?"; my $zipfile = shift or die "zipfile?"; my $secret_file = 'secret.txt'; # use -q (silent mode) # use -o (overwrite mode) o unzip hangs if secret file already exists my $cmd = "unzip -qoP $password $zipfile 2>/dev/null"; print ".cmd: [$cmd]\n"; system($cmd); my $status = ($? >> 8); # status of cmd, like POSIX WIFEXITED() mac +ro if ($status) { die "**ERROR: unzip failed! status = [$status]"; } print ".okay: got secret file! status = [$status]\n"; -f $secret_file && print qx(cat -evnt $secret_file), "\n";

    note that unzip (at least on my cygwin) returns 9 if it cannot find the zip file, and the program above gets that code correctly:

    % stephan@ape (/home/stephan/t0) % % perl safe_unzip.px sesame safe1.zip .cmd: [unzip -qoP sesame safe1.zip 2>/dev/null] **ERROR: unzip failed! status = [9] at safe_unzip.px line 18.
    hth --stephan