subproceso de Python para principiantes: “error de escritura: tubería rota”

Gracias a las sugerencias útiles a continuación:

Así que parece que se arregla cuando

  1. comandos separados en llamadas individuales a Popen
  2. stderr = subprocess.PIPE como un 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) 

Mensaje original:

He pasado demasiado tiempo tratando de resolver un problema canalizando un subproceso simple.

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

Salida para archivo ~ 1000 líneas de longitud:

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

Salida para archivo> 241 líneas de longitud:

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

La salida para el archivo <241 líneas de longitud es buena.

He estado leyendo los documentos y buscando en Google como locos, pero hay algo fundamental en el módulo de subproceso que me falta … tal vez hacer con buffers. He intentado p.stdout.flush () y jugando con el tamaño del búfer y p.wait (). He intentado reproducir esto con comandos como ‘dormir 20; cat moderado ‘, pero esto parece funcionar sin error.

De las recetas en documentos de subproceso :

 # 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] 

Esto se debe a que no debe usar “tuberías de shell” en el comando que se pasa a subprocess.Popen , debe usar el subprocess.PIPE esta manera:

 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 estás tratando de hacer podría hacerse en python puro en lugar de llamar a un grupo de comandos de shell.

He estado teniendo el mismo error. Incluso ponga la tubería en un script de bash y ejecute eso en lugar de la tubería en Python. De Python obtendría el error de la tubería rota, de bash no.

Me parece que quizás el último comando antes de la cabecera esté lanzando un error, ya que (la clasificación) STDOUT está cerrado. Python debe estar recogiendo esto mientras que con el shell el error es silencioso. He cambiado mi código para consumir toda la entrada y el error desapareció.

Tendría sentido que los archivos más pequeños funcionen ya que la tubería probablemente amortigua toda la salida antes de que salga la cabecera. Esto explicaría los cortes en archivos más grandes.

por ejemplo, en lugar de un ‘head -1’ (en mi caso, solo quería la primera línea), hice un awk ‘NR == 1’

Probablemente hay mejores formas de hacerlo dependiendo de dónde se encuentre el ‘cabeza -X’ en la tubería.

No necesitas shell=True . No invocar el shell. Así es como lo haría:

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

¿Ves si te enfrentas al problema del búfer después de usar esto?

intente usar la comunicación () , en lugar de leer directamente desde la salida estándar.

los doctores de python dicen esto:

“Advertencia Use comunicarse () en lugar de .stdin.write, .stdout.read o .stderr.read para evitar interlockings debido a que cualquiera de los otros buffers de tuberías del sistema operativo se llenen y bloqueen el proceso secundario”.

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