python subprocess: "write error: Broken pipe"

Question:

I have a problem piping a simple subprocess.Popen.

Code:

import subprocess
cmd = 'cat file | sort -g -k3 | head -20 | cut -f2,3' % (pattern,file)
p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
for line in p.stdout:
    print(line.decode().strip())

Output for file ~1000 lines in length:

...
sort: write failed: standard output: Broken pipe
sort: write error

Output for file >241 lines in length:

...
sort: fflush failed: standard output: Broken pipe
sort: write error

Output for file <241 lines in length is fine.

I have been reading the docs and googling like mad but there is something fundamental about the subprocess module that I’m missing … maybe to do with buffers. I’ve tried p.stdout.flush() and playing with the buffer size and p.wait(). I’ve tried to reproduce this with commands like ‘sleep 20; cat moderatefile’ but this seems to run without error.

Asked By: mathtick

||

Answers:

From the recipes on subprocess docs:

# To replace shell pipeline like output=`dmesg | grep hda`
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
Answered By: Paulo Scardine

You don’t need shell=True. Don’t invoke the shell. This is how I would do it:

p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
stdout_value = p.communicate()[0] 
stdout_value   # the output

See if you face the problem about the buffer after using this?

Answered By: user225312

This is because you shouldn’t use “shell pipes” in the command passed to subprocess.Popen, you should use the subprocess.PIPE like this:

from subprocess import Popen, PIPE

p1 = Popen('cat file', stdout=PIPE)
p2 = Popen('sort -g -k 3', stdin=p1.stdout, stdout=PIPE)
p3 = Popen('head -20', stdin=p2.stdout, stdout=PIPE)
p4 = Popen('cut -f2,3', stdin=p3.stdout)
final_output = p4.stdout.read()

But i have to say that what you’re trying to do could be done in pure python instead of calling a bunch of shell commands.

Answered By: mdeous

try using communicate(), rather than reading directly from stdout.

the python docs say this:

“Warning Use communicate() rather than
.stdin.write, .stdout.read or
.stderr.read to avoid deadlocks due to
any of the other OS pipe buffers
filling up and blocking the child
process.”

http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout

p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output =  p.communicate[0]
for line in output:
    # do stuff
Answered By: Corey Goldberg

I have been having the same error. Even put the pipe in a bash script and executed that instead of the pipe in Python. From Python it would get the broken pipe error, from bash it wouldn’t.

It seems to me that perhaps the last command prior to the head is throwing an error as it’s (the sort) STDOUT is closed. Python must be picking up on this whereas with the shell the error is silent. I’ve changed my code to consume the entire input and the error went away.

Would make sense also with smaller files working as the pipe probably buffers the entire output before head exits. This would explain the breaks on larger files.

e.g., instead of a ‘head -1’ (in my case, I was only wanting the first line), I did an awk ‘NR == 1’

There are probably better ways of doing this depending on where the ‘head -X’ occurs in the pipe.

Answered By: Chris Beecroft
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.