IOError: [Errno 32] Broken pipe when piping: `prog.py | othercmd`
I have a very simple Python 3 script:
f1 = open('a.txt', 'r')
print(f1.readlines())
f2 = open('b.txt', 'r')
print(f2.readlines())
f3 = open('c.txt', 'r')
print(f3.readlines())
f4 = open('d.txt', 'r')
print(f4.readlines())
f1.close()
f2.close()
f3.close()
f4.close()
But it always says:
IOError: [Errno 32] Broken pipe
I saw on the internet all the complicated ways to fix this, but I copied this code directly, so I think that there is something wrong with the code and not Python's SIGPIPE.
I am redirecting the output, so if the above script was named "open.py", then my command to run would be:
open.py | othercommand
Solution 1:
The problem is due to SIGPIPE handling. You can solve this problem using the following code:
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL)
Update: As pointed out in the comments, python docs already have a good answer.
See here for background on this solution. Better answer here.
Solution 2:
To bring information from the many helpful answers together, with some additional information:
-
Standard Unix signal
SIGPIPE
is sent to a process writing to a pipe when there's no process reading from the pipe (anymore).- This is not necessarily an error condition; some Unix utilities such as
head
by design stop reading prematurely from a pipe, once they've received enough data. - Therefore, an easy way to provoke this error is to pipe to
head
[1]; e.g.:python -c 'for x in range(10000): print(x)' | head -n 1
- This is not necessarily an error condition; some Unix utilities such as
-
By default - i.e., if the writing process does not explicitly trap
SIGPIPE
- the writing process is simply terminated, and its exit code is set to141
, which is calculated as128
(to signal termination by signal in general) +13
(SIGPIPE
's specific signal number). -
However, by design Python itself traps
SIGPIPE
and translates it into a PythonBrokenPipeError
(Python 3) /IOError
(Python 2) instance witherrno
valueerrno.EPIPE
.- Note: If you use a Unix emulation environment on Windows, the error may surface differently - see this answer.
-
If a Python script does not catch the exception, Python outputs error message
BrokenPipeError: [Errno 32] Broken pipe
(Python 3, possibly twice, withException ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
sandwiched in between) /IOError: [Errno 32] Broken pipe
(Python 2) and terminates the script with exit code1
[2] - this is the symptom Johannes (the OP) saw.
Windows considerations (SIGPIPE
is a Unix-only signal)
-
If your script needs to run directly on Windows too, you may have to conditionally bypass code that references
SIGPIPE
, as shown in this answer. -
If your script runs in a Unix subsystem on Windows, the
SIGPIPE
signal may surface differently than on Unix - see this answer.
There are two ways to solve this problem:
Generally, it is not advisable to silence this exception, as it may signal a severe error condition, depending on your script's purpose, such as the receiving end of a network socket unexpectedly closing.
- However, if your script is a command-line utility, where quiet termination may not only be acceptable but preferred so as to play nicely with the standard
head
utility, for instance, you can abort quietly as follows, usingsignal.signal()
to install the platform's default signal handler (which behaves as described above), as also shown in akhan's answer (works in both Python 3 and 2):
# ONLY SUITABLE FOR COMMAND-LINE UTILITIES
# Install the default signal handler.
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)
# Start printing many lines.
# If this gets interrupted with SIGPIPE,
# the script aborts quietly, and the process exit code is set to
# 141 (128 + SIGPIPE)
for x in range(10000): print(x)
- Otherwise, if you want to handle the SIGPIPE-triggered exception yourself (works in both Python 3 and 2, adapted from the docs):
import sys, os, errno
try:
# Start printing many lines.
for x in range(10000): print(x)
# IMPORTANT: Flush stdout here, to ensure that the
# SIGPIPE-triggered exception can be caught.
sys.stdout.flush()
except IOError as e:
# Note: Python 3 has the more specific BrokenPipeError,
# but this way the code works in Python 2 too.
if e.errno != errno.EPIPE: raise e # Unrelated error, re-throw.
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
# ... perform other handling.
# Note: You can't write to stdout here.
# (print() and sys.stdout.write won't work)
# However, sys.stderr.write() can be used.
sys.stderr.write("SIGPIPE received, terminating.\n")
# Finally, exit with an exit code of choice.
sys.exit(141)
[1] Note that in bash
you will by default only see head
's exit code - which is 0
- reflected in $?
afterwards. Use echo ${PIPESTATUS[0]}
to see Python's exit code.
[2] Curiously, on macOS 10.15.7 (Catalina), with Python 3.9.2 (but not 2.x), I see exit code 120
, but the docs say 1
, and that's what I also see on Linux.
Solution 3:
I haven't reproduced the issue, but perhaps this method would solve it: (writing line by line to stdout
rather than using print
)
import sys
with open('a.txt', 'r') as f1:
for line in f1:
sys.stdout.write(line)
You could catch the broken pipe? This writes the file to stdout
line by line until the pipe is closed.
import sys, errno
try:
with open('a.txt', 'r') as f1:
for line in f1:
sys.stdout.write(line)
except IOError as e:
if e.errno == errno.EPIPE:
# Handle error
You also need to make sure that othercommand
is reading from the pipe before it gets too big - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer
Solution 4:
A "Broken Pipe" error occurs when you try to write to a pipe that has been closed on the other end. Since the code you've shown doesn't involve any pipes directly, I suspect you're doing something outside of Python to redirect the standard output of the Python interpreter to somewhere else. This could happen if you're running a script like this:
python foo.py | someothercommand
The issue you have is that someothercommand
is exiting without reading everything available on its standard input. This causes your write (via print
) to fail at some point.
I was able to reproduce the error with the following command on a Linux system:
python -c 'for i in range(1000): print i' | less
If I close the less
pager without scrolling through all of its input (1000 lines), Python exits with the same IOError
you have reported.
Solution 5:
I feel obliged to point out that the method using
signal(SIGPIPE, SIG_DFL)
is indeed dangerous (as already suggested by David Bennet in the comments) and in my case led to platform-dependent funny business when combined with multiprocessing.Manager
(because the standard library relies on BrokenPipeError being raised in several places). To make a long and painful story short, this is how I fixed it:
First, you need to catch the IOError
(Python 2) or BrokenPipeError
(Python 3). Depending on your program you can try to exit early at that point or just ignore the exception:
from errno import EPIPE
try:
broken_pipe_exception = BrokenPipeError
except NameError: # Python 2
broken_pipe_exception = IOError
try:
YOUR CODE GOES HERE
except broken_pipe_exception as exc:
if broken_pipe_exception == IOError:
if exc.errno != EPIPE:
raise
However, this isn't enough. Python 3 may still print a message like this:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe
Unfortunately getting rid of that message is not straightforward, but I finally found http://bugs.python.org/issue11380 where Robert Collins suggests this workaround that I turned into a decorator you can wrap your main function with (yes, that's some crazy indentation):
from functools import wraps
from sys import exit, stderr, stdout
from traceback import print_exc
def suppress_broken_pipe_msg(f):
@wraps(f)
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except SystemExit:
raise
except:
print_exc()
exit(1)
finally:
try:
stdout.flush()
finally:
try:
stdout.close()
finally:
try:
stderr.flush()
finally:
stderr.close()
return wrapper
@suppress_broken_pipe_msg
def main():
YOUR CODE GOES HERE