This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: os.popen('yes | echo hello') stuck
Type: behavior Stage: test needed
Components: Library (Lib) Versions: Python 2.6
process
Status: closed Resolution: wont fix
Dependencies: Superseder: subprocess should have an option to restore SIGPIPE to default action
View: 1652
Assigned To: Nosy List: cpegeric, gregory.p.smith, ilgiz, isandler, r.david.murray
Priority: normal Keywords:

Created on 2007-06-13 13:41 by cpegeric, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (11)
msg32314 - (view) Author: Eric (cpegeric) Date: 2007-06-13 13:41
after calling os.popen('yes | echo hello'), the process never returns.
msg32315 - (view) Author: Ilguiz Latypov (ilgiz) Date: 2007-06-13 17:07

Could you try the same command from your shell?

I am guessing "yes" will write an error message about the broken pipe to stderr.

$ python -c 'import sys, os; sys.stdout.write(os.popen("yes | echo hello").read())'
yes: standard output: Broken pipe
yes: write error
hello

$ python -c 'import sys, os; sys.stdout.write(os.popen3("yes | echo hello")[1].read())'
hello
msg32316 - (view) Author: Eric (cpegeric) Date: 2007-06-14 01:28
command line also get stuck.  The OS is MacOSX.  Python version 2.3.5.  Will it help update to new version?

I quited the program by Ctrl-C.

 python -c 'import sys, os; sys.stdout.write(os.popen("yes | echo hello").read())'
^CTraceback (most recent call last):
  File "<string>", line 1, in ?
KeyboardInterrupt



% python -c 'import sys, os; sys.stdout.write(os.popen3("yes | echo hello")[1].read())'
^CTraceback (most recent call last):
  File "<string>", line 1, in ?
KeyboardInterrupt

msg32317 - (view) Author: Ilguiz Latypov (ilgiz) Date: 2007-06-14 02:24
I've realized my suggestion was not precise.  I meant asking to run the argument to popen() rather than popen().

That is, I would run this from command line to see if there is an issue with the shell:

$ yes | echo hello

msg32318 - (view) Author: Eric (cpegeric) Date: 2007-06-14 02:46
sorry for the misunderstanding.

Here is the result. 

% yes | echo hello
hello
% echo $?
0
%
msg32319 - (view) Author: Ilguiz Latypov (ilgiz) Date: 2007-06-14 03:15

It appears your "yes" does not catch SIGPIPE.  I've reproduced the issue with

$ python -c 'import sys, os; sys.stdout.write(os.popen("while :; do echo yes ; done | echo hello").read())'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
KeyboardInterrupt

The documentation on Python module signal says SIGPIPE is ignored by Python as well.  My work-around to this was to catch SIGPIPE, but the GNU C library would terminate the whole script.

$ python -c 'import sys, os, signal; signal.signal(signal.SIGPIPE, signal.SIG_DFL); sys.stdout.write(os.popen("while :; do echo yes ; done | echo hello").read())'
hello

http://www.gnu.org/software/libc/manual/html_node/Operation-Error-Signals.html

Perhaps, writing own signal handler might help.
msg32320 - (view) Author: Ilguiz Latypov (ilgiz) Date: 2007-06-14 03:22
The SIG_DFL default handler seems to terminate the current process (argument to popen()), not Python script.
msg32321 - (view) Author: Eric (cpegeric) Date: 2007-06-14 03:49
It works now. thank you.
msg99642 - (view) Author: Ilya Sandler (isandler) Date: 2010-02-21 04:40
I don't think this is a bug in python (see below for analysis). Furthermore, os.popen() is deprecated, so I think this issue can be closed.


Here is my understanding of what's happening.

When you execute :

 python -c 'import sys, os; sys.stdout.write(os.popen("while :; do echo  yes ; done | echo hello").read())'

popen() forks and then execs() a /bin/sh like this

 /bin/sh -c "while :; do echo  yes ; done | echo hello"

But exec() (on Linux at least) inherits the signal handling from the pre-exec process for the signals which were set to SIG_IGN or SIG_DFL (see e.g here: http://www.gnu.org/software/libc/manual/html_node/Initial-Signal-Actions.html), so in this case shell will inherit SIG_IGN setting from python for SIGPIPE.

Furthermore, the "sh" manpage explicitly says that shell will wait for all processes in the pipeline. 

So, the sequence of events will be as follows: echo exits, SIGPIPE is delivered to the shell and is ignored by the shell and so the shell keeps running the while loop forever, so .read() call never reaches the eof and your script blocks.

The original "yes|echo" example on MacOsX has likely been caused by the same sequence of events. (if "yes" inherits signal handling from shell, then "yes|echo"
would not block when invoked from command line, but would block when invoked from python)

Installling your own SIGPIPE handler (or resetting SIGPIPE to SIG_DFL as ilgiz suggested) should work around this issue.
msg99646 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2010-02-21 05:29
See also issue 1652, noting as well that os.popen is implemented via subprocess in py3k.
msg100054 - (view) Author: Gregory P. Smith (gregory.p.smith) * (Python committer) Date: 2010-02-24 17:42
issue 1652 will take care of this problem.  i'm working on it.
History
Date User Action Args
2022-04-11 14:56:24adminsetgithub: 45090
2010-02-24 17:42:46gregory.p.smithsetstatus: open -> closed

nosy: + gregory.p.smith
messages: + msg100054

superseder: subprocess should have an option to restore SIGPIPE to default action
resolution: wont fix
2010-02-21 05:29:56r.david.murraysetpriority: normal
nosy: + r.david.murray
messages: + msg99646

2010-02-21 04:40:20isandlersetnosy: + isandler
messages: + msg99642
2009-03-31 01:19:34ajaksu2setpriority: normal -> (no value)
stage: test needed
type: behavior
versions: + Python 2.6, - Python 2.3
2007-06-13 13:41:20cpegericcreate