tchrist@convex.COM (Tom Christiansen) (02/17/91)
I was trying to set up an automatic pager for STDOUT, and it doesn't seem to work. If I choose a different handle, it's fine. Any clues? $pager = $ENV{'PAGER'} || 'more'; open(STDOUT, "| $pager") || die "can't reopen STDOUT to $pager: $!"; open(STDERR, ">&PIPE") || die "dup failed: $!"; system 'ps t'; system 'grep nada nonesuch'; while (<>) { print; } close(STDOUT) || die "close failed: $!"; Notice that I never die of a bad dup even though there is no PIPE handle. If you %s/STDOUT/PIPE/g and add an appropriate select, it all works fine. This is under both 3.044 and 4.0beta. --tom -- Tom Christiansen tchrist@convex.com convex!tchrist "All things are possible, but not all expedient." (in life, UNIX, and perl)
merlyn@iwarp.intel.com (Randal L. Schwartz) (02/17/91)
In article <1991Feb16.210445.7760@convex.com>, tchrist@convex (Tom Christiansen) writes: | I was trying to set up an automatic pager for STDOUT, and | it doesn't seem to work. If I choose a different handle, | it's fine. Any clues? | | $pager = $ENV{'PAGER'} || 'more'; | open(STDOUT, "| $pager") || die "can't reopen STDOUT to $pager: $!"; | open(STDERR, ">&PIPE") || die "dup failed: $!"; | system 'ps t'; | system 'grep nada nonesuch'; | while (<>) { print; } | close(STDOUT) || die "close failed: $!"; | | Notice that I never die of a bad dup even though there is no | PIPE handle. If you %s/STDOUT/PIPE/g and add an appropriate | select, it all works fine. This is under both 3.044 and 4.0beta. Arrrgh... Think about what $pager's stdout is at the time of the fork! This works: open(TMP,"| $pager"); # $pager's stdout is Perl's stdout open(STDOUT, ">&TMP"); # Perl's stdout is now the pager's stdin close(TMP); # no need for this anymore (The appropriate error checking is left as an exercise to the reader.) print "Just another Perl hacker," -- /=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\ | on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III | | merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn | \=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/
emv@ox.com (Ed Vielmetti) (02/18/91)
Not exactly a followup to the above, but here goes. I have a pager set up for some particular output, like so open(PAGER,$pager) || warn "meaningful warning"; print PAGER (join("\n",@result),\n); close(PAGER); The result can be kind of long-winded, so I'd like to let people interrupt out of the pager and still be returned gracefully to the main loop of my script. As it stands now, if $pager = "|cat" and you control-C the output, you lose. Is it a matter of eval'ing something, or some kind of signal handler to set up, or something else I'm missing. I wouldn't think that a ^C sent to cat would leak back up to perl. --Ed
tchrist@convex.COM (Tom Christiansen) (02/18/91)
From the keyboard of emv@ox.com (Ed Vielmetti): :Not exactly a followup to the above, but here goes. : :I have a pager set up for some particular output, like so : open(PAGER,$pager) || warn "meaningful warning"; : print PAGER (join("\n",@result),\n); : close(PAGER); : :The result can be kind of long-winded, so I'd like to let people :interrupt out of the pager and still be returned gracefully to the :main loop of my script. As it stands now, if $pager = "|cat" and you :control-C the output, you lose. : :Is it a matter of eval'ing something, or some kind of signal handler :to set up, or something else I'm missing. I wouldn't think that a ^C :sent to cat would leak back up to perl. Yes, you do indeed need to set up a signal handler. You probably want one for INT, maybe QUIT, and probably for PIPE as well. The PIPE is in case your $pager should bail out early or be absent. Unlike most applications of kill(2), a ^C will cause the the signal to be delivered to the entire process group, not just to the "active" process. This means that both the pager and perl will see it. I think that people who expect otherwise have been lulled into this belief by the behavior of system(3), which will ignore keyboard interrupts and quits. You don't normally need an eval for this kind of thing, nor would it really help you without the signal handler. However, one thing I have done along those lines is something like this: sub PLUMBER { die "caught SIG$_[0] -- plumber bailing out"; } $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'QUIT'} = 'PLUMBER'; eval 'do something()'; die $@ if $@ && $@ !~ /bailing out/; This way I can in effect longjmp all the way back up to the call to something(), without having to muck about with getting all the intervening routines to return gracefully. Used in this way, you can think of die as raising an exception its caller can catch if they wish. --tom -- Tom Christiansen tchrist@convex.com convex!tchrist "All things are possible, but not all expedient." (in life, UNIX, and perl)
lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/19/91)
In article <1991Feb17.230545.16898@convex.com> tchrist@convex.COM (Tom Christiansen) writes:
: Unlike most applications of kill(2), a ^C will cause the the signal to be
: delivered to the entire process group, not just to the "active" process.
: This means that both the pager and perl will see it. I think that people
: who expect otherwise have been lulled into this belief by the behavior of
: system(3), which will ignore keyboard interrupts and quits.
Perl blocks SIGINT and SIGQUIT while waiting for pipes to shut down and
while executing the system operator, just like system(3). It does not
block signals on an output pipe until you do the close, however. If you
have less than 4k to page, you can just shove it down the pipe and close,
but much more than 4k (typically) and you'll block before getting to the
close. So you have to ignore the signals yourself. Saying
local($SIG{'INT'}) = 'IGNORE';
local($SIG{'QUIT'}) = 'IGNORE';
in the right spot should suffice.
Larry
lwall@jpl-devvax.jpl.nasa.gov (Larry Wall) (05/10/91)
In article <1991Feb16.210445.7760@convex.com> tchrist@convex.COM (Tom Christiansen) writes:
: I was trying to set up an automatic pager for STDOUT, and
: it doesn't seem to work. If I choose a different handle,
: it's fine. Any clues?
:
: $pager = $ENV{'PAGER'} || 'more';
: open(STDOUT, "| $pager") || die "can't reopen STDOUT to $pager: $!";
: open(STDERR, ">&PIPE") || die "dup failed: $!";
: system 'ps t';
: system 'grep nada nonesuch';
: while (<>) { print; }
: close(STDOUT) || die "close failed: $!";
:
: Notice that I never die of a bad dup even though there is no
: PIPE handle. If you %s/STDOUT/PIPE/g and add an appropriate
: select, it all works fine. This is under both 3.044 and 4.0beta.
The problem is that open(STDOUT, "| $pager") implicitly closes STDOUT
before calling mypopen() to run $pager, so the pager has no standard
output. It's not exactly a bug, nor is it exactly a misfeature, but
it's enough of a violation of expectations that I think it's worth
special casing, along with the corresponding STDIN behavior.
Basically it comes down to this: you have to call pipe() before you
fork(), so the only two ways to make sure the pipe is attached to
the right fd are 1) close the fd first, or 2) close the fd later and
dup2 the pipe onto it.
A similar situation holds with
open(STDERR, ">&PIPE") || die "dup failed: $!";
in which STDERR is closed before the dup is attempted, so the die dies
without a whimper. I s'pose that needs some TLC too.
Larry