2012-05-07 12 views
8

Estoy tratando de llamar a un script de shell en python, pero sigue informando error de canal roto (el resultado es correcto, pero no quiero ver el mensaje de error en STDERR). He identificado la causa, y puede ser reproducido como el siguiente fragmento:Python: subprocess.call tubería rota

subprocess.call('cat /dev/zero | head -c 10 | base64', shell=True)

AAAAAAAAAAAAAA ==

gato: error de escritura: Tubo roto

/dev/zero es un flujo infinito, pero el head -c 10 solo lee 10 bytes de él y sale, luego cat obtendrá SIGPIPE debido a que el par ha cerrado el conducto. No hay un mensaje de error de canal roto cuando ejecuto el comando en shell, pero ¿por qué Python lo muestra?

+4

El error desaparece cuando se salta el [uuoc] (https://en.wikipedia.org/wiki/ Cat_% 28Unix% 29 # Useless_use_of_cat): 'subprocess.call ('head -c 10

+2

@larsmans: podría poner eso como una respuesta –

+0

@ChrisMorgan: en realidad, Yo prefiero tu respuesta. –

Respuesta

2

En este caso trivial al menos no está obteniendo nada mediante el uso de comandos de shell — y está perdiendo portabilidad y velocidad.

Python 2 código:

>>> import base64 
>>> base64.b64encode(open('/dev/zero', 'rb').read(10)) 
'AAAAAAAAAAAAAA==' 
>>> base64.b64encode('\0' * 10) 
'AAAAAAAAAAAAAA==' 

En Python 3 (código también funcionar en 2.6+, aunque regresará str en lugar de bytes casos):

>>> import base64 
>>> base64.b64encode(open('/dev/zero', 'rb').read(10)) 
b'AAAAAAAAAAAAAA==' 
>>> base64.b64encode(b'\0' * 10) 
b'AAAAAAAAAAAAAA==' 

En cada caso, la primer ejemplo retiene el uso de /dev/zero (en sí mismo no portátil, pero no importa), el segundo produce el efecto, aunque me imagino que no es lo que quieres específicamente?

+0

+1. ''\ x00'' también se puede escribir'' \ 0'' (o 'b '\ 0'' en Python 3.x). –

+1

'\ 0' es más corto que' \ x00', supongo ... –

+0

Creo que prefiero la respuesta de subdir, que aborda la pregunta directamente y explica el problema. –

6

La acción predeterminada para señal SIGPIPE es terminar el programa. El intérprete de Python lo cambia a SIG_IGN para poder informar los errores de pipas rotas a un programa en forma de excepciones.

Cuando ejecuta cat ... |head ... en shell, cat tiene controlador de SIGPIPE predeterminado, y el kernel de sistema operativo simplemente lo termina en SIGPIPE.

Cuando se ejecuta utilizando catsubprocess deriva manejador SIGPIPE de su matriz (intérprete de python), SIGPIPE es simplemente ignorados y cat controla el error en sí por el control de errno mensaje de error variable y la impresión.

Para evitar mensajes de error de cat se pueden utilizar preexec_fn argumento para subprocess.call:

from signal import signal, SIGPIPE, SIG_DFL 
subprocess.call(
    'cat /dev/zero | head -c 10 | base64', 
    shell = True, 
    preexec_fn = lambda: signal(SIGPIPE, SIG_DFL) 
) 
+0

Excelente, muchas gracias por eso, acabo de resolver mi problema con un script de shell usando cat (y estoy bastante seguro de que no es una uuoc, ¡tal vez sea otra pregunta!). –

+0

realmente excelente explicación y solución. –

+0

¡Excelente! Se corrigió un tr: error de pipa interrumpida provocada al invocar un script bash con subprocess.call() – Hanynowsky

Cuestiones relacionadas