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
如果a
, b
和c
是通常从命令行访问的命令,那么您可以使用:
captured_output = `a | b | c`
Ruby将在子shell中运行命令,并捕获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') #=> [#, # , # , #] >> 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#dup
, IO#fileno
, IO#pipe
, IO#reopen
等来复制这些fd操作技巧。它可能更容易重用用于Ruby解释器的C .so库中的shell(例如dash)代码,而不是仅使用Ruby原语组合相同的东西。
我不知道任何现有的通用Ruby API用于复杂的进程间管道/重定向。 如果您可以建议您想要使用的API,可能我可以参与实施。