简述
使用1.9.2版本,并等待PTY进程正确设置$?。
PTY.spawn(command) do |r,w,pid|
Process.wait(pid)
end
完整故事
在1.9.2中,您可以通过在PTY pid上调用wait来捕获PTY的退出状态。这几乎在大多数情况下都有效(据我所知)。我知道的唯一异常情况是像立即退出或发出空字符串命令的边缘情况(请参见http://redmine.ruby-lang.org/issues/5253)。
例如:
require 'pty'
require 'test/unit'
class PTYTest < Test::Unit::TestCase
def setup
system "true"
assert_equal 0, $?.exitstatus
end
def pty(cmd, &block)
PTY.spawn(cmd, &block)
$?.exitstatus
end
def test_pty_with_wait_correctly_sets_exit_status_for_master_slave_io
status = pty("printf 'abc'; exit 8") do |r,w,pid|
while !r.eof?
r.getc
end
Process.wait(pid)
end
assert_equal 8, status
end
def test_pty_with_wait_correctly_sets_exit_status_for_basic_commands
status = pty("true") do |r,w,pid|
Process.wait(pid)
end
assert_equal 0, status
status = pty("false") do |r,w,pid|
Process.wait(pid)
end
assert_equal 1, status
end
def test_pty_with_wait_sets_exit_status_1_for_immediate_exit
status = pty("exit 8") do |r,w,pid|
Process.wait(pid)
end
assert_equal 1, status
end
def test_pty_with_kill
status = pty("sleep 10") do |r,w,pid|
Process.kill(9, pid)
Process.wait(pid)
end
assert_equal nil, status
end
end
现在运行测试:
$ ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin10.8.0]
$ ruby example.rb
Loaded suite example
Started
....
Finished in 1.093349 seconds.
4 tests, 9 assertions, 0 failures, 0 errors, 0 skips
Test run options: --seed 31924
在1.8.7版本中,您需要进行更多操作。在较旧的Ruby版本中,即使您等到PTY进程完成,PTY仍然经常会出现PTY :: ChildExited错误。因此,如果按照编写的方式运行测试,则会出现以下情况:
$ ruby -v
ruby 1.8.7 (2010-08-16 patchlevel 302) [i686-darwin10.4.0]
$ ruby example.rb
Loaded suite example
Started
EE.E
Finished in 1.170357 seconds.
1) Error:
test_pty_with_kill(PTYTest):
PTY::ChildExited: pty - exited: 35196
example.rb:11:in `test_pty_with_kill'
example.rb:11:in `spawn'
example.rb:11:in `pty'
example.rb:45:in `test_pty_with_kill'
2) Error:
test_pty_with_wait_correctly_sets_exit_status_for_basic_commands(PTYTest):
PTY::ChildExited: pty - exited: 35198
example.rb:11:in `test_pty_with_wait_correctly_sets_exit_status_for_basic_commands'
example.rb:11:in `spawn'
example.rb:11:in `pty'
example.rb:26:in `test_pty_with_wait_correctly_sets_exit_status_for_basic_commands'
3) Error:
test_pty_with_wait_sets_exit_status_1_for_immediate_exit(PTYTest):
PTY::ChildExited: pty - exited: 35202
example.rb:11:in `test_pty_with_wait_sets_exit_status_1_for_immediate_exit'
example.rb:11:in `spawn'
example.rb:11:in `pty'
example.rb:38:in `test_pty_with_wait_sets_exit_status_1_for_immediate_exit'
4 tests, 5 assertions, 0 failures, 3 errors
几乎所有测试都会报错ChildExited,只有一个测试(恰好是最真实使用PTY的测试)能如预期一样成功。这种不稳定的行为肯定是个bug,并且已经在1.9.2版本中修复了,正如前面所示。
但是,仍然有一种部分解决方案。您可以使用以下类似代码来专门处理ChildExited错误:
def pty(cmd, &block)
begin
PTY.spawn(cmd, &block)
$?.exitstatus
rescue PTY::ChildExited
$!.status.exitstatus
end
end
插入该代码,再次运行测试,您将得到与1.9.2一致的结果,但有一个重要的警告,即$?无法正确设置(不像1.9.2)。特别是,如果您添加以下测试:
def test_setting_of_process_status
system "true"
assert_equal 0, $?.exitstatus
begin
PTY.spawn("false") do |r,w,pid|
Process.wait(pid)
end
rescue PTY::ChildExited
end
assert_equal 1, $?.exitstatus
end
在1.9.2中成功,在1.8.7中失败。在1.8.7的情况下,通过ChildExited错误完成PTY -- Process.wait从未被调用,因此永远不会设置$?。相反,“system“true””中的$?持续存在,你将得到0而不是1作为退出状态。
$?的行为很难跟踪,并且有更多的警告(例如,有时PTY确实会通过Process.wait完成)。