Path: utzoo!attcan!uunet!know!cs.utexas.edu!sun-barr!newstop!texsun!letni!mic!convex!convex.COM From: tchrist@convex.COM (Tom Christiansen) Newsgroups: comp.lang.perl Subject: Re: Help a perl apprentice Message-ID: <107608@convex.convex.com> Date: 24 Oct 90 02:07:49 GMT References: <18840001@hp-lsd.COS.HP.COM> <10080@jpl-devvax.JPL.NASA.GOV> Sender: news@convex.com Reply-To: tchrist@convex.COM (Tom Christiansen) Organization: CONVEX Software Development, Richardson, TX Lines: 77 In article <10080@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes an excellent analysis of how to optimize a beginning perl script. One little picky point I have is the pipe open. I first posted about this kind of thing in <5016@convex.convex.com> entitled "Testing successful pipe opens" on 31 January for archive nuts. It's been awhile, so I'll bring it up again for people who weren't reading then. > open (BDF_PIPE, "bdf @ARGV |"); > >or better, > > open (BDF_PIPE, "bdf @ARGV |") || die "Can't run bdf: $!\n"; > That will almost never fail. You might get back EGAIN or EPROCLIM due to fork failure, but far more often, the fork succeeds but the execvp fails, and you get no notification of this. This is exactly what happened to me when I ran the script, since on my system, the command is "df" not "bdf". This is one of those little gotchas in perl. One solution is to test for whether $? is set after the close. Note that Larry said that close is not strictly necessary on an input pipe. This, I would say, is a good enough reason to close it and make sure the close went well. Thus: close BDF_PIPE || die "bdf pipe didn't close: $?"; although at PL18 the close doesn't correctly fail on setting $?; it does do the right thing at later patches. For earlier versions, use: close BDF_PIPE; die "bdf pipe didn't close: $?" if $?; You can also find out at the time of the open, although this is trickier. A solution for a command without metacharacters would be like this: ($pid = open (BDF_PIPE, "bdf @ARGV |")) && kill(0, $pid) || die "can't start pipe: $!"; or for people preferring C syntax to shell: if (!($pid = open (BDF_PIPE, "bdf @ARGV |")) || !kill(0, $pid)) { die "can't start pipe: $!"; } If, however, there are shell meta-characters, the shell may not yet have failed by the time the kill happens, so for that case you would want to introduce a delay with sleep, (although I dislike it) as in: ($pid = open (BDF_PIPE, "cmd >foo |")) && (sleep(1), kill(0, $pid)) || die "can't start pipe: $!"; or ($pid = open(BDF_PIPE, "$cmd >$foo |")) || die "fork failed: $!"; sleep 1; kill(0, $pid) || die "cmd \"$cmd > $foo\" failed: $?"; Note that if you're opening for output to a non-existent command, you will eventually get hit with a SIGPIPE. I often install a plumber function for this: sub plumber { die "$0: plumber: broken pipe"; } SIG{'PIPE'} = 'plumber'; I thought that now that we have caller(), I should be able to catch where this actually happened to me, but that doesn't seem to work. It complains that "There is no caller" when I try to use caller from the &plumber routine, which is too bad. --tom