2010-11-20 2 views
16

Rubyでシェルパイピングを自動化する方法はありますか? Rubyパイプ:2つのサブプロセスの出力を結合するにはどうすればよいですか?

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

が、私がこれまでに発見した唯一の解決策は、バッファ管理を自分で行うことです(未テスト、簡略化され、それは全体の私の意味を取得します願っています):私は、Rubyに次のシェルコードを変換しようとしている

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

私が探しているのは、新しいものを作成するのではなく、既存のストリームを使用するようにpopenに指示する方法でしょうか?あるいは、aの出力をbの入力に接続するIO#マージメソッド?私の現在のアプローチは、フィルタの数が増えたときにはかなり扱いにくくなります。

私は明らかに約Kernel#system('a | b')を知っていますが、一般的な方法でRubyフィルタと外部プログラムフィルタを混在させる必要があります。

+0

ruby​​にspawnコマンドを含む1つのソリューションがあります。いくつかのパイプを開き、子プロセスのStdinとStdoutをリダイレクトするためにspawnオプションを使用する必要があります。より詳細な例は私のここで私の答えで見つけることができます:http://stackoverflow.com/questions/11898528/marshal-ruby-pipes-sending-serialized-object-to-child-processes/13258047#13258047 –

+0

Open3のパイプラインはかなり見えます理想的なhttps://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html#method-c-pipeline_rw – kch

答えて

3

a場合は、b、およびcは、あなたが使用することができ、通常はコマンドラインからアクセスするコマンドです:

captured_output = `a | b | c` 

Rubyは、サブシェルでコマンドを実行し、STDOUTをキャプチャします。

何らかの理由で出力をファイルにルーティングする必要がある場合は、コマンドにリダイレクトを追加することもできます。 STDOUTはそのような場合には、あなたに返却されることはありませんが、あなたは、ファイルを開いて、それを手動で処理することができます:

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

それはsystemまたはpopen3を使用してできるだけ多くのコントロールを提供していませんが、それは便利です:

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

私はスレッドを使用する代わりに、すべてのものに大きなループを使用しています...厄介な問題のための厄介な解決策 – mpapis

0

悲しいことに、シェルの配管は深刻な問題であり、実際には相当量のコードが必要になります。読み書きループでスレッドを生成する必要はありませんが、まだ多くの作業が必要です。

私が見つけた最も簡単な例はredirect implementation in dash (Debian Almquist Shell)です。一般的に、Rubyで同じことをしたい場合は、IO#dupIO#filenoIO#pipeIO#reopenなどを使ってfd操作トリックを複製する必要があります。シェル(ダッシュなど)を再利用する方が簡単かもしれませんが、 Rubyプリミティブを使って同じものを結合しようとするよりも、RubyインタプリタのためのC .soライブラリのコードです。

私は複雑なプロセス間パイプライン/リダイレクトのための既存の汎用Ruby APIについて知らない。あなたが使用したい良いAPIを提案できれば、私は実装に参加できるかもしれません。

9

古い質問が、Googleの最初の結果のその1以来、ここでの答えは次のとおりです。http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3/(法8)要するに

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

そして、あなたのような、より複雑なことを行うことができます平野ルビーを使用して

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

spawnあなたは、パイプとプロセスを接続するために使用することができますリダイレクトオプションがあります。

1)2つの生成されたプロセスにもちろん

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

を接続するために使用します)

r,w = IO.pipe 

2のパイプを作成し、あなたが言及したシェルライブラリからsh.systemのようなものでこれをカプセル化することができ、かつ相互接続を行うための|()メソッドを作成します。

標準ライブラリのopen3モジュールには、完全なパイプラインの作成を含め、この種のものには本当に素晴らしいツールがいくつかあります。

関連する問題