2010-11-05 14 views
12

Gracias a las sugerencias útiles a continuación:novato pitón subproceso: "error de escritura: Tubo roto"

Por lo tanto, parece estar fijo cuando

  1. comandos separados en llamadas individuales a Popen
  2. stderr = subproceso.PIPE como argumento para cada cadena de Popen.

el nuevo código:

import subprocess 
import shlex 
import logging 

def run_shell_commands(cmds): 
    """ Run commands and return output from last call to subprocess.Popen. 
     For usage see the test below. 
    """ 
    # split the commands 
    cmds = cmds.split("|") 
    cmds = list(map(shlex.split,cmds)) 

    logging.info('%s' % (cmds,)) 

    # run the commands 
    stdout_old = None 
    stderr_old = None 
    p = [] 
    for cmd in cmds: 
     logging.info('%s' % (cmd,)) 
     p.append(subprocess.Popen(cmd,stdin=stdout_old,stdout=subprocess.PIPE,stderr=subprocess.PIPE)) 
     stdout_old = p[-1].stdout 
     stderr_old = p[-1].stderr 
    return p[-1] 


pattern = '"^85567  "' 
file = "j" 

cmd1 = 'grep %s %s | sort -g -k3 | head -10 | cut -d" " -f2,3' % (pattern, file) 
p = run_shell_commands(cmd1) 
out = p.communicate() 
print(out) 

Post original:

he pasado demasiado tiempo tratando de resolver un problema de tuberías de un subprocess.Popen sencilla.

Código:

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()) 

de salida para el archivo ~ 1000 líneas de longitud:

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

salida de archivos> 241 líneas de longitud:

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

de salida para el archivo de < 241 las líneas de longitud están bien.

He estado leyendo los documentos y buscando en Google como loco, pero hay algo fundamental sobre el módulo de subproceso que me falta ... tal vez para hacer con los búferes. Probé p.stdout.flush() y jugué con el tamaño del búfer y p.wait(). Intenté reproducir esto con comandos como 'dormir 20'; cat moderatefile 'pero parece que se ejecuta sin error.

+0

... y p2.communicate() también funciona, pero creo que puede causar problemas si el resultado es grande. – mathtick

+1

'Nuevo código' muy útil. Me encanta que pueda usar el mismo comando de tuberías que utilicé para probar en el caparazón. Dos sugerencias: 1) make plural: run_shell_commands 2) eliminar, comentar o agregar debug = false alrededor de las instrucciones print dentro de la función – PeterVermont

+1

Gracias. Se encontró con el mismo problema de tubería rota con archivos de un cierto tamaño. Usó su código y funciona como un encanto. – poof

Respuesta

10

De las recetas en 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] 
+2

¿Pensé que se debía evitar la comunicación para obtener grandes resultados? – mathtick

+1

El shell no estaba causando el problema pero por alguna razón dividir los comandos en el lugar "correcto" parece arreglarlo. ¡Gracias! – mathtick

+0

@mathtick: debería iterar sobre PIPE en lugar de atribuir una salida grande a alguna instancia de cadena, de lo contrario correría el riesgo de una excepción de memoria insuficiente. –

0

Usted no necesitan shell=True. No invocar el caparazón. Así es como lo haría:

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

¿Ve si enfrenta el problema sobre el búfer después de usar esto?

+0

El shell no parecía estar causando el problema. Dividir los comandos en el lugar correcto parece haberlo arreglado (ver actualización). ¡Gracias! – mathtick

4

Esto se debe a que no se debe utilizar "las del shell" en el comando que se pasa a subprocess.Popen, se debe utilizar el subprocess.PIPE así:

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() 

pero tengo que decir que lo que usted está tratando de se podría hacer en python puro en lugar de llamar a un grupo de comandos de shell.

+3

Estoy recogiendo más de 13 millones de líneas que devuelven 100k + líneas de partidos, clasificando, cortando y tomando una "cabeza". Esto lleva unos segundos en el caparazón. Tardaría una eternidad en Python. He intentado leer() y pensé que traté de dividir los comandos, pero creo que es el mismo problema. Volveré a publicar después de probar más ... – mathtick

+1

La división de los comandos parece haberlo solucionado, incluso si sigo usando shell = True. – mathtick

0

intente utilizar communicate(), en lugar de leer directamente desde stdout.

los documentos pitón dicen esto:

"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 
+0

Probé p.communicate() [0] pero esto no solucionó el problema. Dividir los comandos de forma adecuada (ver más arriba). Todavía no entiendo por qué esto ha arreglado las cosas. – mathtick

1

He estado teniendo el mismo error.Incluso puso la tubería en un script bash y lo ejecutó en lugar del tubo en Python. De Python obtendría el error de tubería rota, de bash no lo haría.

Me parece que quizás el último comando anterior al encabezado arroja un error ya que (el tipo) STDOUT está cerrado. Python debe estar retomando esto mientras que con el shell el error es silencioso. Cambié mi código para consumir toda la entrada y el error desapareció.

Tendría sentido también con archivos más pequeños, ya que la tubería probablemente almacena toda la salida antes de que la cabeza salga. Esto explicaría las interrupciones en archivos más grandes.

por ejemplo, en lugar de una 'cabeza -1' (en mi caso, yo estaba queriendo solamente la primera línea), hice una awk 'NR == 1'

probablemente hay mejores maneras de hacer esto dependiendo de dónde se produce el 'head -X' en la tubería.