Module: Vectory::Capture
- Defined in:
- lib/vectory/capture.rb
Class Method Summary collapse
-
.with_timeout(*cmd) ⇒ Object
rubocop:disable all.
Class Method Details
.with_timeout(*cmd) ⇒ Object
rubocop:disable all
Originally from gist.github.com/pasela/9392115
Capture the standard output and the standard error of a command. Almost same as Open3.capture3 method except for timeout handling and return value. See Open3.capture3.
result = capture3_with_timeout([env,] cmd... [, opts])
The arguments env, cmd and opts are passed to Process.spawn except opts, opts, opts, opts and opts. See Process.spawn.
If opts is specified, it is sent to the command’s standard input.
If opts is true, internal pipes are set to binary mode.
If opts is specified, SIGTERM is sent to the command after specified seconds.
If opts is specified, it is used instead of SIGTERM on timeout.
If opts is specified, also send a SIGKILL after specified seconds. it is only sent if the command is still running after the initial signal was sent.
The return value is a Hash as shown below.
{
:pid => PID of the command,
:status => Process::Status of the command,
:stdout => the standard output of the command,
:stderr => the standard error of the command,
:timeout => whether the command was timed out,
}
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/vectory/capture.rb', line 40 def with_timeout(*cmd) spawn_opts = Hash === cmd.last ? cmd.pop.dup : {} opts = { :stdin_data => spawn_opts.delete(:stdin_data) || "", :binmode => spawn_opts.delete(:binmode) || false, :timeout => spawn_opts.delete(:timeout), :signal => spawn_opts.delete(:signal) || :TERM, :kill_after => spawn_opts.delete(:kill_after), } in_r, in_w = IO.pipe out_r, out_w = IO.pipe err_r, err_w = IO.pipe in_w.sync = true if opts[:binmode] in_w.binmode out_r.binmode err_r.binmode end spawn_opts[:in] = in_r spawn_opts[:out] = out_w spawn_opts[:err] = err_w result = { :pid => nil, :status => nil, :stdout => nil, :stderr => nil, :timeout => false, } out_reader = nil err_reader = nil wait_thr = nil begin Timeout.timeout(opts[:timeout]) do result[:pid] = spawn(*cmd, spawn_opts) wait_thr = Process.detach(result[:pid]) in_r.close out_w.close err_w.close out_reader = Thread.new { out_r.read } err_reader = Thread.new { err_r.read } in_w.write opts[:stdin_data] in_w.close result[:status] = wait_thr.value end rescue Timeout::Error result[:timeout] = true pid = spawn_opts[:pgroup] ? -result[:pid] : result[:pid] Process.kill(opts[:signal], pid) if opts[:kill_after] unless wait_thr.join(opts[:kill_after]) Process.kill(:KILL, pid) end end ensure result[:status] = wait_thr.value if wait_thr result[:stdout] = out_reader.value if out_reader result[:stderr] = err_reader.value if err_reader out_r.close unless out_r.closed? err_r.close unless err_r.closed? end result end |