I have a ruby timeout that calls a system (bash) command like this..
Timeout::timeout(10) {
`my_bash_command -c12 -o text.txt`
}
but I think that even if the ruby thread is interrupted, the actual command keeps running in the background.. is it normal? How can I kill it?
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'
end
rescue 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
end
end
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
Handling processes, signals and timers is not very easy. That's why you might consider delegating this task: Use the command timeout
on new versions of Linux:
timeout --kill-after 5s 10s my_bash_command -c12 -o text.txt