[Tutor] "standard output: Broken pipe"

Eric Brunson brunson at brunson.com
Mon Oct 22 16:33:39 CEST 2007


Martin Walsh wrote:
> Eric Brunson wrote:
>   
>> I'm coming in late to the discussion and thought that someone would 
>> explain it succinctly, but there have been so many correct statements 
>> which I feel fail to nail down the problem that I thought I'd chime in.
>>
>>     
>
> Hi Eric,
>
> Thank you for your considerate response. Your analysis is spot on --
> speaking for myself, of course -- I'm mostly just flailing here; not
> having a very strong grasp of how all the components work
> together (kernel, shell, python, and the subprocess module). But I'm
> really enjoying this thread and learning a lot in the 'process' ...
> ahem, sorry for the pun ... painful :)
>
>   
>> Here's the important concepts to understand.  The pipe is a construct of 
>> the shell, you are spawning a shell to run your entire command line.  
>> When the "make" finishes and stops accepting the input from "yes", the 
>> kernel sends a SIGPIPE to the parent process, i.e. the shell.  The 
>> shell's default behavior when receiving the SIGPIPE is to print an error 
>> message.
>>     
>
> You're right. Considering the shell is definitely important and I missed
> it. Yet given the following, slightly adapted from the subprocess module
> docs (your reference link below) ...
>
> from subprocess import Popen, PIPE
> p1 = Popen(["yes", "Hello"], stdout=PIPE)
> p2 = Popen(["head", "-n", "10"], stdin=p1.stdout, stdout=PIPE)
> output = p2.communicate()[0]
>
> ... still produces ...
>
> yes: standard output: Broken pipe
> yes: write error
>   

Looks like the "yes" command is getting the SIGPIPE and not the shell, 
as I originally thought.  I must have glossed over the "yes:" at the 
beginning.

> If run as a script, the message above shows up in the terminal after the
> python script terminates (or apparently so; shell prompt first, then
> error). And when using the interactive interpreter... nothing until
> exiting the interpreter, when the message again appears in the terminal.
> Perhaps this is some peculiarity of my environment, or more likely I am
> still missing something.
>
>   
>> Your problem, as I interpret it, is that you don't like seeing the error 
>> message.  So you have two choices:  1) find some way to filter it, 
>> because all it is is a message, or 2) get rid of the shell and handle 
>> the management of the subprocesses yourself.  It's been discussed how to 
>> suppress the output and it's been discussed how to intercept the signal, 
>> but I don't thing that anyone has pointed to the definitive python 
>> construct to handle it properly. 
>>
>> You've had several responses that correctly tell you how to handle a 
>> signal, but without any testing I'm pretty sure that none of that will 
>> do you any good with the subprocess.call() construct you're using.  The 
>> shell is your subprocess and it is already handling the signal, so you 
>> should never see it.  The trick is to spawn both processes via python 
>> *without* an intervening shell to interfere.  Here's the canonical 
>> example: http://docs.python.org/lib/node536.html.  You can spawn each 
>> subprocess, 'yes' and 'make' without the shell being required to pipe 
>> the output of one to the other, you can do it all completely in python.
>>     
>
> Based on my own tests, I don't think it makes a difference. I suspect
> the python override for sigpipe is inherited by all child processes,
> shell or otherwise, and the SIG_IGN is allowing 'yes' to see the broken
> pipe, report it and terminate -- where the default action would normally
> be to terminate only. This is, of course, not that far from wild
> speculation. Though, I am curious if others observe the same behavior.
>   

I'd have to break out some documentation to review signal mask 
inheritance, it's been so long it escapes me.

> Then again, the error is *not* displayed for any of tests where
> signal.signal(signal.SIGPIPE, signal.SIG_DFL) is added, in either the
> global scope or in the form of a preexec_fn argument passed to
> subprocess, with shell=True or without.
>
> IMHO, this is certainly a tripping point if true. Granted, probably a
> fringe case and relatively rare since it requires spawning a subprocess,
> which writes to a pipe that is broken before the subprocess ends, and is
> not implemented to handle the event gracefully. But I still find it
> weirdly intriguing.
>   

Since "yes" is designed to spit out its output indefinitely, it seems 
like a bad behavior to print a message on SIGPIPE.  It's always going to 
happen, it's the expected result.  The versions of "yes" on my linux box 
here and a solaris box at work don't seem to elicit the same error message.

Just another reason why I would look for a solution that would avoid the 
"yes" altogether.  :-)

>> Again, sorry to come in late, but while reading many absolutely factual 
>>     
>
> <off-topic-blather>
> As far as I'm concerned, no apologies are necessary -- not that you are
> apologizing to me necessarily :) But nonetheless, thank you for helping.
>
> In fact, a big thank you to all the tutors. I've been a member of the
> list for quite a few years now, reaping the benefits of your collective
> experience (like many others I would imagine), mostly by lurking. You
> provide a tremendous service to the python community, especially those
> new to python or programming in general, with near super-human patience
> and skill. Quite a feat! FWIW, thanks.
> </off-topic-blather>
>   

Even being one of the more vocal on the list, I'll add my vote to your 
commendations.  I learn just as much on the list as I feel I teach, it's 
a great forum with some great participants.

> Marty
>
>   
>> an correct responses, they all seemed to be either handling the output 
>> or discussing signal handling without, in my mind, pointing out that 
>> it's the shell that's giving you the problems.  I think it's Python's 
>> default behavior to ignore SIGPIPE, as Martin comments on in his latest 
>> reply.  It's just that the shell has already accepted and handled the 
>> signal.
>>
>> Interestingly, when you eliminate the shell and use something similar to 
>> the example code from above, you can pretty easily see how to get rid of 
>> the 'yes' and just feed the make subprocess input yourself, further 
>> simplifying the code.
>>
>> I'll admit, I've stated all this without actually running any test, so 
>> please, if anyone can show differently, I'm happy to have my 
>> understanding corrected.
>>
>> I hope that helps, not only with your specific problem, but from a "big 
>> picture" standpoint, also.  Nothing I hate more than writing code that I 
>> don't really understand.  :-)
>>
>> e.
>>
>> James wrote:
>>     
>>> Hi,
>>>
>>> I have a snippet of code in a Python script I'm whipping up that's  
>>> causing a not-so-pretty output.  Here's the code:
>>>
>>> subprocess.call( "yes '' | make oldconfig" , shell=True )
>>>
>>> When I run this code, Python loyally executes the command, and then I  
>>> see the following error on my console:
>>>
>>> -----
>>>
>>> yes: standard output: Broken pipe
>>> yes: write error
>>>
>>> -----
>>>
>>> I did some Googling and I believe found the reason for this error  
>>> ("yes" executes forever, and complains when Python kills the process  
>>> off).  However, I'd like to figure out a way to get rid of the error  
>>> (or hide it) so that it's not visible to the person running the  
>>> script (it's not the prettiest thing to see scroll by your screen :)).
>>>
>>> Thoughts / ideas?
>>>
>>> Thanks!
>>> .james
>>> _______________________________________________
>>> Tutor maillist  -  Tutor at python.org
>>> http://mail.python.org/mailman/listinfo/tutor
>>>   
>>>       
>> _______________________________________________
>> Tutor maillist  -  Tutor at python.org
>> http://mail.python.org/mailman/listinfo/tutor
>>     
>
>
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> http://mail.python.org/mailman/listinfo/tutor
>   



More information about the Tutor mailing list