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

I am using open to pipe to "more". (See example in Programming Perl 3ed, pg 426)
my $pager = "garbage"; open(FH,"| $pager") || die("Can't fork a $pager: $!"); print FH "Writing to $pager"; close(FH) || die("Can't close FH: $!");

The problem I am having is that, garbage is not executable and so the open should fail and die should execute. This doesn't happen. Then of course the print fails as would the close. Shouldn't open fail if it can't pipe to $pager? What can I do if I am just wrong here and open will not fail? In what instance would the open fail for this code?

I appreciate all help with this.

Thanks,

Shannon

Replies are listed 'Best First'.
Re: Problems with open
by Abigail-II (Bishop) on Jul 24, 2002 at 15:00 UTC
    It will die if you have 5.6.0 or later.

    What is happening is this: the open will open a pipe, fork, and the child will exec "garbage". On perls older than 5.6.0, open will return the pid - that is, it will return true if the fork succeeded.

    Since 5.6.0, if you pipe open a simple program (one which doesn't require the shell to resolve special characters), and said program doesn't exist, the handling is special cased and the open itself will fail.

    open my $fh => "| garbage *" or die;
    will not die, because Perl will fork and exec a shell (which will succeed), asking the shell to execute garbage * (which can't be done because "garbage" doesn't exist).

    Abigail

      Thanks a lot! So it looks like I need a work around for this. I am using Perl 5.005_02. Any ideas?

      -Shannon
        Well, you could upgrade.... ;-).

        If you know the command after the pipe is going to be simple (just letters, digits and whitespace), you could test whether the first word exists somewhere in the current path and is executable. Or peek in the source of a modern perl and see how's it dealt with and mimic that.

        But note that even recent perls only handle cases that don't involve the shell.

        Abigail

Re: Problems with open
by kodo (Hermit) on Jul 24, 2002 at 15:08 UTC
    Uhm I've got perl 5.6.1 here on a win32-box (activestate perl) and it doesn't die when I have:
    my $pager = "garbage"; open(FH,"| $pager") || die("Can't fork a $pager: $!"); print FH "Writing to $pager"; close(FH) || die("Can't close FH: $!");
    and garbage doesn't exist. Also I get the PID returned but maybe this is a win32-issue.
    You could simply do a check before the open, a la:
    my $pager = "garbage"; if (-x $pager) { open(FH,"| $pager") || die("Can't fork a $pager: $!"); print FH "Writing to $pager"; close(FH) || die("Can't close FH: $!"); }


    giant
      I am using Perl 5.005_02 on a SunOS 5.6 machine. I thought of the -x pager but if the $pager does not contain the full path to whatever pager you are using (like less) then this is not going to work. I was hoping to find a cleaner way of doing this, but sounds like we are on the right track. I know we could always throw in a which, but like I said, I would like to find a cleaner way.

      Thanks,

      -sk
        I guess if you wanted to check if $garbage is a valid exe before executing it you could do something like the following. I'm not sure what the behavior is on 5.002 with File::Spec - can probably replace that with split(/:/,$ENV{'PATH'});
        use File::Spec; sub valid_exe { my $exe = shift; for my $dir ( File::Spec->path() ) { my $f = File::Spec->catfile($dir,$exe); if( -e $f && -x $f ) { # it is a valid exe return 1; } } return 0; }
        Essentially what which will do for you, your call if it is cleaner.
Re: Problems with open
by bronto (Priest) on Jul 24, 2002 at 15:51 UTC

    Your code dies for me:

    $ perl test.pl Can't exec "garbage": No such file or directory at test.pl line 6. Can't fork a garbage: No such file or directory at test.pl line 6.

    I am running perl 5.6.1 on Debian Linux 3.0

    Just a small adding to other monks' comments. Like other functions, print has a return value, too. So you shouldn't limit yourself at checking if open succeeds or fails: you should check print also, and take actions when it fails

    Just my 2c/Euro
    --bronto

    # Another Perl edition of a song:
    # The End, by The Beatles
    END {
      $you->take($love) eq $you->made($love) ;
    }

      This appears to be an issue with Perl pre 5.6. I am running 5.005_2 on SunOS 5.6. With diagnostics on, I get:

      Can't exec "garbage": No such file or directory at foo.pl line 8 (#1) (W) An system(), exec(), or piped open call could not execute the +named program for the indicated reason. Typical reasons include: the pe +rmissions were wrong on the file, the file wasn't found in $ENV{PATH}, the executable in question was compiled for another architecture, or t +he #! line in a script points to an interpreter that can't be run for similar reasons. (Or maybe your system doesn't support #! at all. +)


      -sk
Re: Problems with open
by cybear (Monk) on Jul 24, 2002 at 14:57 UTC
    I am thinking that the open might not fail, because it is only opening a link to $pager.
    Until you actually try to send something to $pager, open is (may not be) doing anything with $pager
    that would cause a failure.
      What do you mean with "opening a link to $pager"?

      Abigail

      The problem is is that the handle is not open when I try to print which tells me the open failed. The $pager (more) is a child of the FH process (which opened a pipe to whatever). If the child $pager failed, shouldn't that be returned and the "open" fail? Like my other question asks, in what case would this fail? The | works fine everytime and so the first chance for failure would come with the opening of $pager, right?

      Thanks,

      -Shannon
Re: Problems with open
by simeon2000 (Monk) on Jul 24, 2002 at 14:59 UTC
    Are you running a UNIX system? Knowing the OS you're coding on would help with the solution.
      Oops. Very sorry about that. Everyone here is in Unix. SunOS 5.6

      -sk