Ruby 有一个似乎是很常见的需求,但是没发现简洁优雅的实现方式:如何结束 ping x.x.x.x -t 命令?

xizhu2012 · 2015年09月26日 · 最后由 msg7086 回复于 2015年09月28日 · 3198 次阅读

需求: windows 系统,在程序中运行命令"ping X.X.X.X -t"10 秒,并在 10 秒后停止 ping,并返回这段时间内 ping 命令的回显。

刚开始觉得挺简单的,设置超时时间,然后调用 ping 命令,到点抛异常并捕获,输出回显。

require 'timeout'
result = ""
begin
    timeout(10) do
        result = `ping 127.0.0.1 -t` #假设ip为127.0.0.1
    end
rescue Exception
    puts result 
    puts $!
end

运行后发现完全不是想象的样子...

execution expired

然后开始上网搜寻解决方法,发现没有一个方法能满足需求,最后在一个英文论坛中找到半个解决方案,只能在 10 秒后结束 ping -t 命令,但获取不了内容,而且代码还很长,涉及到 IO.poen,Process.waitpid2 之类的命令,没看太明白,还有些解决方案中提到用 fork,open3,open4 的方法,比较简洁,但是在 windows 下这些都无法使用,无奈之下,只好来论坛求助. 我觉得这个需求很简单,很常见,因为不一定是 ping -t 命令,凡是涉及到很多耗时的操作,需要中断并取得操作结果的场景,都和这个类似,像这种需求,实现起来应该会有简洁优雅的方式吧,只是我没找到?希望大家能给点帮助,多谢!

rescue Excetion "uninitialized constant Excetion (NameError)" "未初始化的常量 Excetion (名字错啦)"

#1 楼 @nong 抱歉,变量名和 exception 都打错了,更正了下,但结果还是获取不到回显。能给点思路吗?

所以你的需求是调用 shell 运行程序然后超时了再强制杀进程? 你确定这需求很常见?

是我就 ping -c 10 -W 1 算了。

#3 楼 @msg7086 呵呵,调用 shell 再停止很常见吧,只是超时后如何拿到运行结果这个不一定常见. 那就退一步,假设这个需求很不常见,该如何实现呢?多谢赐教!

#4 楼 @rei 恩,不用-t,你的实现方式也能满足 ping 10 秒的要求。之所以一定要求 ping -t,是因为想看看如果推广到其他很耗时的命令时,应该如何杀掉并取得结果。

最简单就是 open3, 可以等待结果,这个也是标准库之一。你要用 windows 的话,基本上再常见的场景都会变成少见,很多只能靠你自己摸索。

#7 楼 @billy 恩,很在理,能理解。只是项目组只能用 windows,而且自己在 windows 下也形成了不小的依赖。如果不行就算了,不再钻牛角尖了

@xizhu2012 你可以用 net/ping 试试,抱歉没在 WINDOWS 下测试过

windows 下没办法用 open3 的话,可以将命令的标准输出到文件

ping 127.0.0.1 -t > log 

然后从这个文件读取即可

net-ping,还需要装一下win32-security

测试可用。

c:\git>irb
irb(main):001:0> require 'net/ping'
=> true
irb(main):002:0> pt = Net::Ping::TCP.new('cvpmesip01')
=> #<Net::Ping::TCP:0x193eee8 @host="cvpmesip01", @port=7, @timeout=5, @exception=nil, @warning=nil, @duration=nil>
irb(main):003:0> pt.ping
=> false
irb(main):004:0> pt = Net::Ping::TCP.new('cvpmesip01',2881)
=> #<Net::Ping::TCP:0x2a798f0 @host="cvpmesip01", @port=2881, @timeout=5, @exception=nil, @warning=nil, @duration=nil>
irb(main):005:0> pt.ping
=> true
irb(main):006:0> pt = Net::Ping::TCP.new('cvpmesip01',80)
=> #<Net::Ping::TCP:0x2b18b90 @host="cvpmesip01", @port=80, @timeout=5, @exception=nil, @warning=nil, @duration=nil>
irb(main):007:0> pt.ping
=> true

Windows 就不考虑了,毕竟用的人太少太少,支持度也很差,更不会生产级使用了。只说 Linux 下的。

最简单的方法就是启动一个进程,拿到 PID,时间一到去杀进程就行了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号