2010-05-16 7 views
17

ho un semplice script di Ruby che assomiglia a questocome si fa a uscita pipe da uno script Ruby per 'testa' senza ottenere un tubo rotto errore

require 'csv' 
while line = STDIN.gets 
    array = CSV.parse_line(line) 
    puts array[2] 
end 

Ma quando cerco di utilizzare questo script in un oleodotto Unix in questo modo, ottengo 10 linee di produzione, seguita da un errore:

ruby lib/myscript.rb < data.csv | head 

12080450 
12080451 
12080517 
12081046 
12081048 
12081050 
12081051 
12081052 
12081054 
lib/myscript.rb:4:in `write': Broken pipe - <STDOUT> (Errno::EPIPE) 

c'è un modo per scrivere lo script Ruby in un modo che impedisce l'eccezione tubo rotto venga sollevata?

risposta

22

head sta chiudendo il flusso di output standard dopo aver letto tutti i dati necessari. Dovresti gestire l'eccezione e interrompere la scrittura sullo standard output. Il seguente codice interrompe il ciclo, una volta uscita standard è stato chiuso:?

while line = STDIN.gets 
    array = CSV.parse_line(line) 
    begin 
    puts array[2] 
    rescue Errno::EPIPE 
    break 
    end 
end 
+0

Se la testa chiude il flusso, allora perché '' $ stdout.closed ancora return false e perché l'errore non accadrà immediatamente, ma solo dopo che molte righe sono state scritte nel vuoto? Penso che head mantenga il flusso aperto, ma non lo legga più, il che fa sì che il buffer sia pieno in un punto che causa il punto rotto. – sepp2k

+5

@ sepp2k - Ci sono probabilmente un paio di cose che succedono qui: 1, la modalità di buffering predefinita per le modifiche di stdout da orientata alla linea verso il blocco quando si utilizza una pipeline, quindi sarà necessario scorrere tra ogni scrittura. 2, 'head' ha bisogno di una possibilità di esecuzione per chiudere il flusso, ma molti più byte di dati potrebbero essere stati scritti prima che abbia la possibilità di funzionare. Ho scritto una variante dello script con '$ stdout.flush; sleep 0.1' tra ogni 'write', e in questo caso' $ stdout.closed? 'funziona. –

+0

@AidanCully: Ah, molto interessante. – sepp2k

10

Il trucco che uso è sostituire head con sed -n 1,10p.

Questo mantiene aperta la tubazione in modo che ruby (o qualsiasi altro programma che verifica la rottura di tubi e lamentele) non ottenga il tubo rotto e pertanto non si lamenta. Scegli il valore che desideri per il numero di linee.

Chiaramente, questo non sta tentando di modificare lo script Ruby. C'è quasi sicuramente un modo per farlo nel codice Ruby. Tuttavia, la tecnica "sed invece di testa" funziona anche dove non si ha la possibilità di modificare il programma che genera il messaggio.

Problemi correlati