0

As noted in comments to these questions:

perl -e '$SIG{PIPE} = "IGNORE"; sleep(1); print "foo\n";' | echo -n

produces error message Unable to flush stdout: Broken pipe.

But weirdly

perl -e '$SIG{PIPE} = sub { print STDERR "XXX\n"; }; sleep(1); print "foo\n";' | echo -n

does not produce any message.

Why the second command does not produce a message? How to catch that SIGPIPE event?

The thing I really need is (for an experiment to answer this question) to catch SIGPIPE and when it happens output something to a log file.

porton
  • 5,214
  • 11
  • 47
  • 95

1 Answers1

2

When writing into a pipe like in your example STDOUT is buffered by default. This means the print "foo\n" in your code does not immediately write to STDOUT but writes into a buffer and the buffer will only be flushed (written to STDOUT) when it is full or as part of the program cleanup. Using strace to see what your program is actually doing shows that it first restores the signal handlers as part of the program cleanup and then writes to STDOUT, resulting in a SIGPIPE which is no longer handled by your sub and thus results in a kill of the program.

When making sure that the output is done immediately and not at the cleanup of the program then your signal handler gets successfully triggered. This can be done by making STDOUT unbuffered using $|=1:

$ perl -e \
   '$|=1; $SIG{PIPE} = sub { print STDERR "XXX\n"; }; sleep(1); print "foo\n";' \
   | echo -n

output: XXX

In other words: if active your signal handler works perfectly and it was only an artefact of your specific use that it got not triggered. On why it does not reset the signal handler from ignore (SIG_IGN) to default (SIG_DFL) on exit and thus shows the same problem with IGNORE I don't know.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • perl only resets the signals that were set to a perl sub/handler, not those that were set to SIG_IGN. (see [here](https://perl5.git.perl.org/perl.git/blob/HEAD:/ext/ExtUtils-Miniperl/lib/ExtUtils/Miniperl.pm#l160)). the idea is probably to prevent a perl signal handler from trying to access the objects already destroyed during the 'global destruction phase'. Anyways, a `flush STDOUT` at the end of the script is probably better than making the `STDOUT` unbuffered with `$| = 1`. –  Sep 25 '18 at 07:39