help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Help-bash] When pipes fail (and when not)


From: Eric Blake
Subject: Re: [Help-bash] When pipes fail (and when not)
Date: Wed, 28 Nov 2018 09:45:56 -0600
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.3.0

On 11/28/18 9:02 AM, Paul Wagner wrote:
On 28.11.2018 13:54, Eric Blake wrote:
I still don't understand why setting the trap on a system that has SIGPIPE vital doesn't work, so I'd be very greatful for any hints.

It's a lame restriction from POSIX based on historical practice (that
I _really_ wish we weren't stuck with):
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap

"Signals that were ignored on entry to a non-interactive shell cannot
be trapped or reset, although no error need be reported when
attempting to do so. An interactive shell may reset or catch signals
ignored on entry. Traps shall remain in place for a given shell until
explicitly changed with another trap command."

Maybe I don't get your point, but I think there might be a missunderstanding:  In my previous post I was referring to my example on Linux, where SIGPIPE is not ignored by default:

Okay, there was indeed some misunderstanding. My quote was about the situation where a bash script starts with sigpipe inherited as ignored - there is NOTHING the script can do to undo that, short of re-execing via an intermediary non-shell process that can lift the sigpipe ignored inheritance before going back into shell. (And that's the situation that most frequently plagues CI test setups, if they leak an ignored sigpipe into child processes under test). The fact that POSIX forbids the shell from undoing an inherited ignored sigpipe is rather awkward, but we are stuck with it.

Now, on to your complaint - when does the trap handler for sigpipe actually fire. Well, that depends on whether bash started with sigpipe ignored - because if it did, you cannot undo that effect short of re-execing.


for i in {0..9}; do echo $i; sleep 1; done | dd bs=1 count=10

ends as expected after 5 iterations, so SIGPIPE is not ignored in the current shell;

I'm running tests with an interactive environment where I _know_ that sigpipe is not ignored from the outside. Note, however, that the subsidiary bash takes a full 10 seconds to complete, regardless of whether sigpipe is inherited ignored or default:

$ time (trap '' pipe; bash -c 'for i in {0..9}; do /bin/echo $i; sleep 1; done | dd bs=1 count=10')
0
1
2
3
4
10+0 records in
10+0 records out
10 bytes copied, 4.00699 s, 0.0 kB/s
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe

real    0m10.033s
user    0m0.026s
sys     0m0.007s

$ time (trap - pipe; bash -c 'for i in {0..9}; do /bin/echo $i; sleep 1; done | dd bs=1 count=10')
0
1
2
3
4
10+0 records in
10+0 records out
10 bytes copied, 4.0058 s, 0.0 kB/s
real    0m10.027s
user    0m0.016s
sys     0m0.012s

So, even when sigpipe is not ignored, and even though the output stops after 5 seconds, the subshell takes the full ten seconds to complete execution because I have not actually stopped the loop, but merely changed what happens with the /bin/echo processes outputting to a pipe that no longer has a reading process.

But, when bash starts with sigpipe ignored, I cannot undo that from within bash:

$ time (trap '' pipe; bash -c 'trap - pipe; for i in {0..9}; do /bin/echo $i; sleep 1; done | dd bs=1 count=10')
0
1
2
3
4
10+0 records in
10+0 records out
10 bytes copied, 4.00624 s, 0.0 kB/s
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe
/bin/echo: write error: Broken pipe

real    0m10.029s
user    0m0.016s
sys     0m0.014s


So, the fact that you were able to install a trap handler that made a difference shows that you are, indeed, in an environment where you did not inherit an ignored sigpipe.


trap '' pipe
for i in {0..9}; do echo $i; sleep 1; done | dd bs=1 count=10

reports a write error five times after the fifth iteration, as expected; but installing another trap like

trap 'echo foo >&2' pipe
for i in {0..9}; do echo $i; sleep 1; done | dd bs=1 count=10

ends after 5 iterations (so SIGPIPE is not trapped, but handled again), but does not write 'foo' to stderr (the terminal), and I don't understand why.

The other consideration is to figure out WHEN the trap handler fires. It does NOT fire every time that /bin/echo dies due to SIGPIPE, but ONLY if the entire pipeline dies due to SIGPIPE. But 'dd' did not die due to SIGPIPE, therefore the trap handler never fires.

The fact that you have installed a trap handler at all determines whether bash lets /bin/echo inherit sigpipe ignored or defaulted, and therefore determines whether /bin/echo outputs an error message; but since the trap handler is NOT firing (because the pipeline itself is not dying due to SIGPIPE, which would only happen if 'dd' exited that way), the loop is executing all ten /bin/echo processes, whether or not the dd process is still around to consume them, and without your handler ever firing.

--
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



reply via email to

[Prev in Thread] Current Thread [Next in Thread]