2010-11-20 70 views
16

有沒有一種自動化的方式在Ruby中執行shell管道?我想轉換下面的shell代碼到Ruby:Ruby管道:我如何將兩個子進程的輸出連接在一起?

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

但到目前爲止,我已經找到了唯一的解決辦法就是做緩衝管理自己(簡體,未經測試,希望它橫跨得到了我的意思):

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使用現有的流,而不是創建一個新的?或者,將#輸出連接到b的輸入的IO#合併方法?當過濾器數量增加時,我目前的方法變得相當不實。

我知道Kernel#system('a | b')很明顯,但我需要以通用的方式將Ruby過濾器與外部程序過濾器混合使用。

+0

在ruby中有一個使用spawn命令的解決方案。你應該打開幾個管道,然後使用產卵選項來重定向Stdin和Stdout子進程。你可以在我的答案中找到更詳細的示例:http://stackoverflow.com/questions/11898528/marshal-ruby-pipes-sending-serialized-object-to-child-processes/13258047#13258047 – 2012-11-07 19:48:50

+0

Open3的管道看起來很漂亮理想的https://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html#method-c-pipeline_rw – kch 2017-05-31 05:33:08

回答

3

如果abc的命令通常通過命令行訪問,那麼你可以使用:

captured_output = `a | b | c` 

紅寶石將在一個子shell中運行的命令,並捕獲標準輸出。

如果由於某種原因需要將輸出路由到文件,那麼您也可以將重定向添加到命令中。 STDOUT不會在這種情況下退還給你,但你可以打開該文件,手動處理它:

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

它不提供儘可能多的控制,使用systempopen3,但它的方便:

>> 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 2010-11-21 01:10:12

0

不幸的是,在shell中管道是一個嚴重的問題,事實上,它需要相當數量的代碼。沒有必要用讀/寫循環產生線程,但它仍然需要很多工作。

我找到的最簡單的例子是redirect implementation in dash (Debian Almquist Shell)。一般來說,如果你想在Ruby中做同樣的事情,你需要使用Ruby的IO#dup,IO#fileno,IO#pipe,IO#reopen等等來複制這些操作技巧。這可能更容易重用shell(比如破折號)代碼在C .so庫中用於Ruby解釋器,而不是僅僅使用Ruby原語來組合相同的東西。

我不知道任何現有的廣義Ruby API用於複雜的進程間管道/重定向。如果你可以建議你想使用一個好的API,可能是我可以參與實施。

9

老問題,但由於在谷歌的第一個結果的一個,這裏是答案: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)創建一個管道

r,w = IO.pipe 

2)用它來連接兩個子進程

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

當然,你也可以在類似sh.system自提殼牌庫封裝這一點,創建一個用於執行互連的|()方法。

標準庫的open3模塊有一些非常好的工具,包括創建完整的管道。

相關問題