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

hi there, I want to understand what this code snippet is doing exactly. I have an idea, but I might be missing something.

open F, "cat file | /usr/bin/mycommand"; my $def_slash = $/; $/ = ""; eval <F>; $/ = $def_slash; close F;

Sorry that I can't post the command itself, maybe that's important in this case:(. My goal to catch any error returned from this command, but $@ does not catch anything after the eval :( Could you please advise? Thanks much

Replies are listed 'Best First'.
Re: Undestanding this code snippet
by moritz (Cardinal) on Jul 21, 2008 at 09:45 UTC
    perlopentut and open should give you some clues.

    You can always catch errors from open like this:

    open HANDLE, ... or die "Can't open: $!";

    Note that $@ is only for errors from eval.

Re: Undestanding this code snippet
by shmem (Chancellor) on Jul 21, 2008 at 10:17 UTC

    Perl supports opening a pipe to or from a shell command, that's what the open call seems to try to accomplish. But for that, a pipe sign must be prepended (in case of writing to) or appended (in case of reading from) to the shell expression, like so:

    open F, "| some_shell_expression"; # open for writing open F, "some_shell_expression |"; # open for reading

    See open for details. Your open as written won't work, since you don't specify the open mode (read or write). The command itself as posted by you features a useless use of cat since it is better written as

    /usr/bin/mycommand < file

    The next line saves the input record separator (see perlvar), sets it to the empty string enabling "slurp mode" on the filehandle F; next all that can be read from F is read and then passed to eval to be executed. For that to work,

    1. something must be read from the filehandle F and
    2. the stuff read must be valid perl code.
    See eval for details.

    I suspect that there's nothing read from F, and evaling an empty string doesn't result in an error stored in $@.

    If you just want to run a command and check it for errors, use system. If system returns something different than 0 (zero), something went wrong. Then exploit $? (see perlvar), along the lines

    my $cmd = "/usr/bin/mycommand < file"; if ( system $cmd ) { warn $cmd . ($? == -1) ? " failed to execute: $!\n" : ($? & 127) ? sprintf "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without' : sprintf "child exited with value %d\n", $? >> 8; }

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Thanks everyone for replying, you are right, there is a "|" as the file handle is opened...apologize that I missed that when I modified the command before posting :( (open F, "some_shell_expression |";)

      With that said, is there still a way for catching that system error with the current code?
      Thanks again!

        Yes - see IPC::Open3. That module provides capturing STDERR of a command. The exit status is available after reaping the child with waitpid. Example (untested):

        my $pid = open3('<&STDIN', \*CHLD_OUT, \*CHLD_ERR, "some_shell_express +ion"); while(<CHLD_OUT>) { # do something with that command's output } my $err = <CHLD_ERR>; # may block - better use select here waitpid $pid, 0; if ( $? ) { warn $_[0] . ($? == -1) ? "failed to execute: $!\n" : ($? & 127) ? sprintf "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without' : sprintf "child exited with value %d\n", $? >> 8; }

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Undestanding this code snippet
by FunkyMonk (Bishop) on Jul 21, 2008 at 09:56 UTC
    It attempts to open a curiously named file "cat file | /usr/bin/mycommand" for reading and executes the perl code it contains.

    I very much doubt that is what you want!

    I think you should look at open and perlopentut.


    Unless I state otherwise, all my code runs with strict and warnings
Re: Undestanding this code snippet
by svenXY (Deacon) on Jul 21, 2008 at 09:55 UTC
    Hi,
    when I run your script with strictures, I get readline() on closed filehandle F at 698996.pl (please check Why you should use strict). I'm not sure if a file handle can be eval'ed at all, but would seriously doubt that it will execute your external code (fellow monks - please correct me if I'm wrong).
    Usually, when trying to catch outpout of an external command, you either use backticks, or - what I prefer - you use open with a pipe (-|), then you can do something like this:
    #!/usr/bin/perl use strict; use warnings; open(F, '-|', "cat file | /usr/bin/mycommand") or die "Something nasty + happened with my command"; while (<F>) { # do something with each line of your output } close F;

    Regards,
    svenXY
Re: Undestanding this code snippet
by LesleyB (Friar) on Jul 21, 2008 at 10:43 UTC

    Hi

    The use of $/ in file slurp mode (as you have up there) is documented here and eval is documented here .

    From this information, I deduce your code snippet reads the whole file in and then executes it as a perl program.