2010-11-20 13 views
16

¿Hay una manera automatizada de hacer tubería de concha en Ruby? Estoy tratando de convertir el siguiente código shell a Ruby:Tubos de rubí: ¿Cómo ato la salida de dos subprocesos?

a | b | c... > ... 

pero la única solución que he encontrado hasta ahora es hacer la gestión de memoria intermedia a mí mismo (simplificado, no probada, la esperanza de que llegue mi sentido de diámetro):

a = IO.popen('a') 
b = IO.popen('b', 'w+') 
Thread.new(a, b) { |in, out| 
    out.write(in.readpartial(4096)) until in.eof? 
    out.close_write 
} 
# deal with b.read... 

Supongo que lo que estoy buscando es una forma de decirle a popen que use una transmisión existente, en lugar de crear una nueva? ¿O, alternativamente, un método de fusión IO # para conectar la salida de a a la entrada de b? Mi enfoque actual se vuelve poco manejable cuando crece la cantidad de filtros.

Sé acerca de Kernel#system('a | b') obviamente, pero necesito mezclar los filtros Ruby con los filtros de programas externos de una manera genérica.

+0

Hay una solución con el comando spawn en ruby. Debería abrir varias tuberías y luego usar las opciones de generación para redirigir Stdin y Stdout de procesos secundarios. Se puede encontrar un ejemplo más detallado en mi respuesta aquí: http://stackoverflow.com/questions/11898528/marshal-ruby-pipes-sending-serialized-object-to-child-processes/13258047#13258047 –

+0

tuberías de Open3 parecen bastante ideales https://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html#method-c-pipeline_rw – kch

Respuesta

3

Si a, b, y c son comandos normalmente se accede desde la línea de comandos, entonces podría usar:

captured_output = `a | b | c` 

Ruby ejecutar los comandos en un sub-shell, y capturar la salida estándar.

Si necesita enrutar la salida a un archivo por alguna razón, puede agregar la redirección al comando también. STDOUT no será devuelto a usted en ese caso, pero se puede abrir el archivo y manualmente procesarlo:

`a | b | c > captured_output` 
File.foreach('captured_output') do |li| 
    print li 
end 

no ofrece tanto control como el uso de system o popen3 pero es conveniente:

>> sin, sout, serr = Open3.popen3('ls -al | tail -1') #=> [#<IO:fd 4>, #<IO:fd 5>, #<IO:fd 7>, #<Thread:0x00000100bb8798 run>] 
>> sout.read #=> "drwxr-xr-x 3 greg staff 102 Nov 2 21:01 python\n" 
+0

también en lugar de utilizar hilos habría hecho uso de bucle grande por todo ... desagradable solución para desagradable problema – mpapis

0

Lamentablemente, la tubería con carcasa es un asunto serio y, de hecho, requeriría una buena cantidad de código. No es necesario engendrar hilos con bucles de lectura/escritura, pero aún requeriría mucho trabajo.

El ejemplo más simple que he encontrado es redirect implementation in dash (Debian Almquist Shell). En general, si lo desea hacer lo mismo en Ruby, que necesita para replicar estos fd trucos de manipulación usando Ruby IO#dup, IO#fileno, IO#pipe, IO#reopen, etc. Podría ser más fácil de reutilizar shell (como el tablero de) código en la biblioteca C .so para el intérprete de Ruby que tratar de combinar lo mismo con solo primitivos de Ruby.

No conozco ninguna API Ruby generalizada existente para tuberías/redireccionamientos entre procesos complejos. Si puede sugerir una buena API que le gustaría usar, podría participar en la implementación.

9

vieja pregunta, pero ya que es uno de los primeros resultados en Google, aquí está la respuesta: http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3/ (método 8)

En resumen:

sh = Shell.new 
sh.system("a") | sh.system("b") | sh.system("c") 

y que puede hacer las cosas más complicadas como

sh.echo(my_string) | sh.system("wc") > "file_path" 
xml = (sh.echo(html) | sh.system("tidy", "-q")).to_s 
1

El uso de rubíes llano, spawn tiene opciones de redirección que se pueden utilizar para conectar con los procesos de tuberías.

1) Crear una tubería

r,w = IO.pipe 

2) Se usa para conectar dos dio lugar a procesos

spawn(*%w[echo hello world], out: w) 
spawn(*%w[tr a-z A-Z], in: r) 
# => HELLO WORLD 

Por supuesto, se puede encapsular esto en algo así como sh.system de la biblioteca Shell mencionado, y crea un método |() para hacer la interconexión.

El módulo open3 de la biblioteca estándar tiene algunas herramientas muy buenas para este tipo de cosas, incluyendo la creación de tuberías completas.

Cuestiones relacionadas