如何在Ruby中模拟Ctrl + C?

时间:2017-04-21 22:42:39

标签: ruby signals

我有一个包含IO.popen调用的Ruby脚本。我可以用CTRL + C取消popen调用(我使用Signal.trap("SIGINT"),然后导致我调用kill -2 #{popen_pid})。这很好用,当我按下CTRL + C时,kill -2会导致popen进程在结束之前运行一些清理。

我的问题:当popen进程耗时太长时,我也希望能够有这种相同的行为。但是,我无法模拟CTRL + C行为。在ruby脚本的pid上使用Process.kill最终只是在没有清理的情况下结束所有事情,并且在popen进程上运行Process.kill什么都不做。我尝试使用反引号直接访问shell,在ruby脚本的pid和popen pid上调用kill -2,但那些也没有做任何事情。

有人有任何建议吗?

2 个答案:

答案 0 :(得分:0)

可能很难得到关于杀死进程的答案,尤其是在访问shell时。一个平台上的解决方案可能无法在其他平台上运行。我建议使用Timeout,而不是试图通过杀死你的进程的麻烦,如果你的IO.popen调用需要的时间比你想要的长,那么你可以在救援中进行清理。

require 'timeout'

seconds_to_wait = 10


begin
  block_result = Timeout.timeout(seconds_to_wait) do
    # your IO stuff
  end
rescue Timeout::Error
  puts "Ain't nobody got time for that!"
  # do your clean up here
end

答案 1 :(得分:0)

事实证明我遇到的问题主要是因为我想在popen过程中打印行,因为他们正在发生这些行,但是还有一个超时会执行kill -2来鼓励进程清理。

最后,我最终在popen调用旁边开始一个新线程(但是在我开始从命令开始打印之前),一旦popen调用花费的时间长于我的等待时间,线程就会调用kill -2

Thread.new do
  $next_thread += 1
  this_thread = $next_thread - 1
  $active_threads[this_thread] = true
  start_time = Time.now
  while true
    break if not $active_threads[this_thread]
    minutes_elapsed = (Time.now - start_time) / 60
    if minutes_elapsed > 90
      inner_packer_pid = `ps -Af | tr -s ' ' | grep -v "grep" | grep "packer build" | cut -d' ' -f2 | sort | tail -n 1`.strip()
      `kill -2 #{inner_packer_pid}`
      cancel_build()
      break
    end
  end
  $active_threads[this_thread] = false
end

builders = Set.new

packer_build = IO.popen("packer build -color=#{not $is_no_color} #{spec_file} 2>&1")
$packer_build_pid = packer_build.pid
packer_build.each do |line|
  puts line
end