ruby timeouts and system commands
I think you have to kill
it manually:
require 'timeout'puts 'starting process'pid = Process.spawn('sleep 20')begin Timeout.timeout(5) do puts 'waiting for the process to end' Process.wait(pid) puts 'process finished in time' endrescue Timeout::Error puts 'process not finished in time, killing it' Process.kill('TERM', pid)end
in order to properly stop spawned process tree (not just the parent process) one should consider something like this:
def exec_with_timeout(cmd, timeout) pid = Process.spawn(cmd, {[:err,:out] => :close, :pgroup => true}) begin Timeout.timeout(timeout) do Process.waitpid(pid, 0) $?.exitstatus == 0 end rescue Timeout::Error Process.kill(15, -Process.getpgid(pid)) false endend
this also allows you to track process status
Perhaps this will help someone else looking to achieve similar timeout functionality, but needs to collect the output from the shell command.
I've adapted @shurikk's method to work with Ruby 2.0 and some code from Fork child process with timeout and capture output to collect the output.
def exec_with_timeout(cmd, timeout) begin # stdout, stderr pipes rout, wout = IO.pipe rerr, werr = IO.pipe stdout, stderr = nil pid = Process.spawn(cmd, pgroup: true, :out => wout, :err => werr) Timeout.timeout(timeout) do Process.waitpid(pid) # close write ends so we can read from them wout.close werr.close stdout = rout.readlines.join stderr = rerr.readlines.join end rescue Timeout::Error Process.kill(-9, pid) Process.detach(pid) ensure wout.close unless wout.closed? werr.close unless werr.closed? # dispose the read ends of the pipes rout.close rerr.close end stdout end