2012-05-07 21 views
8

Sto provando a chiamare uno script di shell in python, ma continua a segnalare errori di pipe non funzionanti (il risultato è OK, ma non voglio vedere il messaggio di errore in STDERR). Ho individuato la causa, e può essere riprodotto come il seguente frammento:Python: subprocess.call pipe interrotte

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

AAAAAAAAAAAAAA ==

gatto: scrivere errore: tubo rotto

/dev/zero è un flusso infinito, ma il head -c 10 legge solo 10 byte da esso ed esce, quindi cat riceverà SIGPIPE perché il peer ha chiuso la pipe. Non c'è nessun messaggio di errore di pipe rotto quando eseguo il comando in shell, ma perché python lo mostra?

+4

L'errore va via quando si salta il [uuoc] (https://en.wikipedia.org/wiki/ Cat_% 28Unix% 29 # Useless_use_of_cat): 'subprocess.call ('testa -c 10

+2

@larsmans: potresti metterlo come risposta –

+0

@ChrisMorgan: in realtà, Preferisco fortemente la tua risposta. –

risposta

2

In questo caso banale almeno non stai guadagnando nulla utilizzando i comandi della shell — e stai perdendo portabilità e velocità.

Python 2 codice:

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

In Python 3 (codice viene eseguito anche in 2.6 +, anche se tornerà str anziché bytes casi):

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

In ogni caso, la il primo esempio mantiene l'uso di /dev/zero (di per sè non portatile, ma non importa), il secondo produce l'effetto, anche se immagino che non sia ciò che vuoi in particolare?

+0

+1. ''\ x00'' può anche essere scritto'' \ 0'' (o 'b '\ 0'' in Python 3.x). –

+1

'\ 0' è più corto di' \ x00', suppongo ... –

+0

Penso di preferire la risposta di subdir, che indirizza direttamente la domanda e spiega il problema. –

6

L'azione predefinita sul segnale SIGPIPE è terminare il programma. L'interprete Python lo cambia in SIG_IGN per essere in grado di segnalare errori di pipe non funzionanti a un programma sotto forma di eccezioni.

Quando si esegue cat ... |head ... nella shell, cat ha il gestore SIGPIPE predefinito e il kernel del sistema operativo lo termina su SIGPIPE.

Quando si esegue utilizzando catsubprocess deriva gestore SIGPIPE dal suo genitore (interprete Python), SIGPIPE è solo ignorato e cat gestisce l'errore in sé verificando errno messaggio di errore variabile e la stampa.

Per evitare messaggi di errore da cat è possibile utilizzare preexec_fn argomento per 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

Eccellente, grazie mille per quello, ho appena risolto il mio problema con uno script di shell usando cat (e sono abbastanza sicuro che non è un uuoc, forse questa è un'altra domanda!). –

+0

spiegazione e soluzione davvero eccellenti. –

+0

Eccellente! Risolto un errore tr: pipe fallito causato chiamando uno script bash con subprocess.call() – Hanynowsky

Problemi correlati