Ruby管道:如何将两个子进程的输出绑定在一起?

是否有自动方式在Ruby中进行shell管道? 我正在尝试将以下shell代码转换为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使用现有流而不是创建新流的方法? 或者,使用IO#merge方法将输出连接到b的输入? 当filter的数量增加时,我当前的方法变得相当笨拙。

我明白知道Kernel#system('a | b') ,但我需要以通用的方式将Rubyfilter与外部程序filter混合在一起。

老问题,但由于它是谷歌的第一个结果之一,这里是答案: 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 

如果abc是通常从命令行访问的命令,那么您可以使用:

 captured_output = `a | b | c` 

Ruby将在子shell中运行命令,并捕获STDOUT。

如果由于某种原因需要将输出路由到文件,则也可以将重定向添加到命令中。 在这种情况下,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') #=> [#, #, #, #] >> sout.read #=> "drwxr-xr-x 3 greg staff 102 Nov 2 21:01 python\n" 

使用普通ruby, spawn具有可用于将进程与管道连接的重定向选项。

1)创建一个管道

 r,w = IO.pipe 

2)使用它来连接两个生成的进程

 spawn(*%w[echo hello world], out: w) spawn(*%w[tr az AZ], in: r) # => HELLO WORLD 

当然,您可以将其封装在类似于sh.system提到的Shell库的sh.system中,并创建一个|()方法来进行互连。

标准库的open3模块为这种东西提供了一些非常好的工具,包括创建完整的管道。

可悲的是,shell中的管道是一个严重的问题,实际上,它需要相当数量的代码。 没有必要使用读/写循环生成线程,但它仍然需要大量的工作。

我发现的最简单的例子是dash (Debian Almquist Shell)中的重定向实现 。 通常,如果你想在Ruby中做同样的事情,你需要使用Ruby的IO#dupIO#filenoIO#pipeIO#reopen等来复制这些fd操作技巧。它可能更容易重用用于Ruby解释器的C .so库中的shell(例如dash)代码,而不是仅使用Ruby原语组合相同的东西。

我不知道任何现有的通用Ruby API用于复杂的进程间管道/重定向。 如果您可以建议您想要使用的API,可能我可以参与实施。