2010-11-20 15 views
16

Esiste un modo automatico per eseguire il piping della shell in Ruby? Sto cercando di convertire il seguente codice shell a Ruby:Tubi color rubino: come legare insieme l'output di due sottoprocessi?

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

ma l'unica soluzione che ho trovato finora è quello di fare la gestione del buffer me (semplificato, non testato, spero che ottiene il mio senso di diametro):

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... 

Immagino che quello che sto cercando sia un modo per dire a popen di usare uno stream esistente, invece di crearne uno nuovo? O in alternativa, un IO # unisce il metodo per connettere l'output di a per l'input di b? Il mio attuale approccio diventa piuttosto poco pratico quando il numero di filtri aumenta.

Sono a conoscenza di Kernel#system('a | b') ovviamente, ma ho bisogno di mescolare i filtri Ruby con i filtri di programma esterni in modo generico.

+0

C'è una soluzione con il comando spawn in ruby. È necessario aprire più pipe e quindi utilizzare le opzioni di spawn per reindirizzare Stdin e Stdout dei processi figli. Puoi trovare un esempio più dettagliato nella mia risposta qui: http://stackoverflow.com/questions/11898528/marshal-ruby-pipes-sending-serialized-object-to-child-processes/13258047#13258047 –

+0

Le condotte di Open3 sembrano belle ideale https://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html#method-c-pipeline_rw – kch

risposta

3

Se a, b, e c sono comandi solitamente accessibili dalla riga di comando allora si potrebbe utilizzare:

captured_output = `a | b | c` 

Ruby eseguire i comandi in una sotto-shell, e catturare STDOUT.

Se è necessario instradare l'output in un file per qualche motivo, è possibile aggiungere anche il reindirizzamento al comando. STDOUT non verrà restituito a voi in quel caso, ma si potrebbe aprire il file e manualmente elaborarla:

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

non offre il massimo controllo utilizzando system o popen3 ma è comodo:

>> 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

Anch'io, invece di usare Threads, l'ho fatto usando il big loop per tutto ... brutta soluzione per problemi sgradevoli – mpapis

0

Purtroppo, le tubazioni con guscio sono un problema serio e, in effetti, richiederebbe una buona quantità di codice. Non è necessario generare thread con cicli di lettura/scrittura, ma richiederebbe comunque molto lavoro.

L'esempio più semplice che ho trovato è redirect implementation in dash (Debian Almquist Shell). Generalmente, se vuoi fare lo stesso in Ruby, dovrai replicare questi fd trucchi di manipolazione usando Ruby IO#dup, IO#fileno, IO#pipe, IO#reopen, ecc. Potrebbe essere più facile riutilizzare la shell (come i trattini) codice nella libreria C .so per interprete Ruby che provare a combinare la stessa cosa usando solo i primitivi Ruby.

Non so di alcuna API Ruby generalizzata esistente per complesse piping/reindirizzamento di interprocessi. Se potresti suggerire una buona API che vorresti utilizzare, potrei essere in grado di partecipare all'implementazione.

9

vecchia questione, ma dal momento che il suo uno dei il primo risultato su Google, ecco la risposta: http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3/ (metodo 8)

In breve:

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

e si possono fare le cose più complicate come

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

Utilizzando rubino pianura, spawn ha opzioni di reindirizzamento che è possibile utilizzare per collegare i processi con i tubi.

1) Creare un tubo

r,w = IO.pipe 

2) Usarlo per collegare due processi generati

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

Naturalmente, è possibile incapsulare questo in qualcosa di simile sh.system dalla libreria Shell citato, e crea un metodo | |) per fare l'interconnessione.

Il modulo open3 della libreria standard ha alcuni strumenti davvero carini per questo genere di cose, compresa la creazione di pipeline complete.