2010-11-20 38 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模块有一些非常好的工具,包括创建完整的管道。

相关问题