2010-05-16 7 views
17

Tengo un simple script Ruby que se parece a esto¿Cómo hacer que la producción de tubería desde un script Ruby a 'cabeza' sin conseguir una tubería rota de error

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

Pero cuando intento usar este script en una tubería de Unix de esta manera, me da 10 líneas de salida, seguido por un error:

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) 

¿hay una manera de escribir el guión de rubíes de una manera que impide la excepción tubería rota de ser levantado?

Respuesta

22

head está cerrando el flujo de salida estándar después de haber leído todos los datos que necesita. Debe manejar la excepción y dejar de escribir en la salida estándar. El siguiente código se abortará el bucle de salida estándar una vez se ha cerrado:?

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

Si la cabeza se cierra la corriente, entonces ¿por qué '' Todavía $ stdout.closed return false y por qué el error no ocurrirá de inmediato, pero sólo después de que se hayan escrito muchas líneas en el vacío? Creo que la cabeza realmente mantiene la secuencia abierta, pero ya no lee, lo que hace que la memoria intermedia esté llena en algún punto que causa el punto roto. – sepp2k

+5

@ sepp2k - Lo más probable es que ocurran un par de cosas aquí: 1, el modo de almacenamiento en búfer predeterminado para cambios estándar desde orientados a la línea a orientados a bloques cuando se utiliza una interconexión, por lo que deberá realizar un ciclo entre cada escritura. 2, 'head' necesita una oportunidad de ejecutarse para cerrar la secuencia, pero es posible que se hayan escrito muchos más bytes de datos antes de que se ejecute. Escribí una variante del script con '$ stdout.flush; duerma 0.1' entre cada 'write', y en este caso' $ stdout.closed? 'funciona. –

+0

@AidanCully: Ah, muy interesante. – sepp2k

10

El truco que uso es reemplazar head con sed -n 1,10p.

Esto mantiene la tubería abierta para que ruby (o cualquier otro programa que compruebe si hay tuberías rotas y quejas) no se rompa y por lo tanto no se queja. Elija el valor que desea para el número de líneas.

Claramente, esto no está intentando modificar su secuencia de comandos de Ruby. Es casi seguro que hay una forma de hacerlo en el código de Ruby. Sin embargo, la técnica 'sed en lugar de cabeza' funciona incluso cuando no tienes la opción de modificar el programa que genera el mensaje.

Cuestiones relacionadas