我使用popen3得到了意想不到的行为,我想用它来运行像工具ala这样的命令cmd < file1 > file2.以下示例挂起,因此stdout done永远不会到达.使用其他cat可能导致悬挂的工具,从而stdin done永远不会达到.我怀疑,我正在缓冲,但我该如何解决这个问题呢?
#!/usr/bin/env ruby
require 'open3'
Open3.popen3("cat") do |stdin, stdout, stderr, wait_thr|
stdin.puts "foobar"
puts "stdin done"
stdout.each_line { |line| puts line }
puts "stdout done"
puts wait_thr.value
end
puts "all done"
Run Code Online (Sandbox Code Playgroud)
sep*_*p2k 14
stdout.each_line正在等待进一步输出,cat因为cat输出流仍然是打开的.它仍处于打开状态,因为cat它仍在等待用户的输入,因为它的输入流尚未关闭(您会注意到当您cat在终端中打开并输入时foobar,它仍然会运行并等待输入,直到您按下^d关闭流).
所以要解决这个问题,只需stdin.close在打印输出之前调用即可.
你的代码挂了,因为stdin仍然是开放的!
如果使用,您需要使用IO#close或关闭它.IO#close_writepopen3
如果你使用popen那么你需要使用,IO#close_write因为它只使用一个文件描述符.
#!/usr/bin/env ruby
require 'open3'
Open3.popen3("cat") do |stdin, stdout, stderr, wait_thr|
stdin.puts "foobar"
stdin.close # close stdin like this! or with stdin.close_write
stdout.each_line { |line| puts line }
puts wait_thr.value
end
Run Code Online (Sandbox Code Playgroud)
也可以看看:
Tilo和sepp2k的答案是对的:如果你关闭stdin,你的简单测试就会结束.问题解决了.
虽然在您对sepp2k答案的评论中,您表明您仍然遇到挂起.好吧,你可能忽略了一些陷阱.
如果你调用一个程序打印更多的stderr而不是匿名管道的缓冲区可以容纳(当前Linux的64KiB),程序将被暂停.暂停的程序既不退出也不关闭stdout.因此,从它的标准读取将会挂起.因此,如果你想做得对,你必须使用线程或IO.select非阻塞,无缓冲的读取,以便并行或轮流读取stdout和stderr而不会卡住.
如果你试图向你的程序()提供比"foobar"更多(更多)的东西cat,stdout的匿名管道的缓冲区将会变满.操作系统将暂停cat.如果你对stdin写的更多,stdin的匿名管道的缓冲区将会满了.然后你的电话stdin.write会被卡住.这意味着:您需要写入stdin,从stdout读取并从stderr并行或轮流读取.
阅读一本好书(Richards Stevens,"UNIX网络编程:进程间通信")并使用好的库函数.IPC(进程间通信)过于复杂,容易出现不确定的运行时行为.试图通过尝试和错误来纠正它是太麻烦了.
使用Open3.capture3.