我使用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"
答案 0 :(得分:13)
stdout.each_line
正在等待cat
的进一步输出,因为cat
的输出流仍处于打开状态。它仍处于打开状态,因为cat
仍在等待来自用户的输入,因为其输入流尚未关闭(当您在终端中打开cat
并输入{{1}时,您会注意到它仍将运行并等待输入,直到您按foobar
关闭流。)
因此,要解决此问题,只需在打印输出之前调用^d
。
答案 1 :(得分:7)
您的代码已挂起,因为stdin
仍处于打开状态!
如果您使用IO#close
,则需要使用IO#close_write
或popen3
关闭它。
如果您使用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
另见:
答案 2 :(得分:5)
Tilo和sepp2k的回答是正确的:如果你关闭stdin
,你的简单测试就会结束。问题解决了。
虽然在对answer of sepp2k的评论中,您表示您仍然遇到挂起。 好吧,你可能忽略了一些陷阱。
如果您调用的程序打印的内容比匿名管道的缓冲区可以容纳的更多(当前Linux的64KiB),程序将被暂停。暂停的程序既不退出也不关闭stdout。因此,从它的标准读取将会挂起。因此,如果你想做得对,你必须使用线程或IO.select
,非阻塞,无缓冲读取,以便并行或轮流读取stdout和stderr而不会卡住。
如果您尝试向程序(cat
)提供比“foobar”更多(更多)的内容,则stdout的匿名管道的缓冲区将变满。操作系统将暂停cat
。如果你对stdin写的更多,stdin的匿名管道的缓冲区将会满了。然后,您对stdin.write
的电话将被卡住。这意味着:您需要写入stdin,从stdout读取并从stderr并行或轮流读取。
阅读一本好书(Richards Stevens,“UNIX网络编程:进程间通信”)并使用好的库函数。 IPC(进程间通信)过于复杂,容易出现不确定的运行时行为。尝试通过尝试和错误来解决这个问题太麻烦了。
使用Open3.capture3
。