我正在尝试通过PTY模块管理与网络设备的SSH连接,代码类似于:
cmd_line = "ssh coltrane@love.supreme.com"
begin
PTY.spawn(cmd_line) do |r_f,w_f,pid|
...
rescue PTY::ChildExited => cended
...
end
整个I / O工作得很好,但是我不知道如何获得子进程的退出状态。
例如,如果连接断开或只是超时,则生成的进程将以错误代码终止,但此代码似乎不会在$?
特殊变量中返回。
答案 0 :(得分:15)
使用1.9.2并等待PTY进程正确设置$?
PTY.spawn(command) do |r,w,pid|
# ...
Process.wait(pid)
end
在1.9.2上,您可以通过在PTY pid上调用wait来捕获PTY的退出状态。几乎所有时间(AFAIK)都有效。我所知道的唯一例外是边缘情况,例如立即退出或为命令发出空字符串(请参阅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上你需要多做一点。在较旧的rubies中,即使您等待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使用的那个)成功按预期成功。当然,这种不稳定的行为是一个错误,正如已经显示的那样,它已在1.9.2中得到修复。
然而,有一个部分解决方法。您可以使用以下内容专门处理ChildExited错误:
def pty(cmd, &block)
begin
PTY.spawn(cmd, &block)
$?.exitstatus
rescue PTY::ChildExited
$!.status.exitstatus
end
end
插入,再次运行测试,得到的结果与1.9.2一致,BIG警告$?将无法正确设置(与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的情况下,PTY通过ChildExited错误完成 - Process.wait永远不会被调用,因此永远不会设置$?。相反,$?从'system“true”'继续存在,你得到0而不是1作为退出状态。
$的行为?很难遵循并有更多的警告,我不会进入(即有时PTY 将通过Process.wait完成)。
答案 1 :(得分:1)
好的,这里有一些可能解决这个问题的方法:
使用ruby 1.9.2 PTY.check()方法
将命令行包装在脚本中
不幸的是我无法使用最新版本的ruby,因此我使用了包装解决方案,它回应了$?到包装器脚本末尾的文件。当生成的子项退出时,将读取退出代码。
当然,如果某些事情中断了包装脚本本身的执行,那么我们永远不会得到结果文件......
但至少这种解决方法可用于1.8.7 / 1.9.1版本的Ruby
答案 2 :(得分:0)
摘要:在Linux中父母应该等待()s让这个孩子知道他孩子的退出状态 C代码:
int status;
等待(&amp; status)//在父代码部分中 WEXITSTATUS(status)//宏返回返回子项的退出代码
对不起。我没有ruby的经验来为您提供一些代码。 祝福:)