Module: Tetra::ProcessRunner
- Includes:
- Logging
- Defined in:
- lib/tetra/facades/process_runner.rb
Overview
runs programs in subprocesses
Instance Method Summary collapse
-
#run(commandline, echo = false, stdin_data = nil) ⇒ Object
runs a noninteractive executable and returns its output as a string raises ExecutionFailed if the exit status is not 0 optionally echoes the executable’s output/error to standard output/error.
-
#run_interactive(command) ⇒ Object
runs an interactive executable in a subshell.
Methods included from Logging
Instance Method Details
#run(commandline, echo = false, stdin_data = nil) ⇒ Object
runs a noninteractive executable and returns its output as a string raises ExecutionFailed if the exit status is not 0 optionally echoes the executable’s output/error to standard output/error
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 |
# File 'lib/tetra/facades/process_runner.rb', line 11 def run(commandline, echo = false, stdin_data = nil) log.debug "running `#{commandline}`" # Prepare buffers to capture output out_buffer = StringIO.new err_buffer = StringIO.new exit_status = nil # Flatten handles nested arrays, Compact removes nils cmd_args = Array(commandline).flatten.compact.map(&:to_s) Open3.popen3(*cmd_args) do |stdin, stdout, stderr, wait_thr| # 1. Handle Input (if any) stdin.write(stdin_data) if stdin_data stdin.close # Close stdin so the process knows input is finished # Use threads to read stdout and stderr simultaneously. readers = [] readers << Thread.new do stdout.each_line do |line| print line if echo # Echo to real STDOUT if requested out_buffer << line # Capture to memory end end readers << Thread.new do stderr.each_line do |line| $stderr.print line if echo # Echo to real STDERR if requested err_buffer << line # Capture to memory end end # Wait for reading to finish readers.each(&:join) # Get the exit code exit_status = wait_thr.value end # Check for failure unless exit_status.success? # Extract strings from buffers out = out_buffer.string err = err_buffer.string log.warn("`#{commandline}` failed with status #{exit_status.exitstatus}") if !out.empty? || !err.empty? log.warn("Output follows:") log.warn(out) unless out.empty? log.warn(err) unless err.empty? end fail ExecutionFailed.new(commandline, exit_status.exitstatus, out, err) end # Return the standard output out_buffer.string end |
#run_interactive(command) ⇒ Object
runs an interactive executable in a subshell
72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/tetra/facades/process_runner.rb', line 72 def run_interactive(command) log.debug "running interactive `#{command}`" # system() passes control to the subprocess cmd_args = Array(command).flatten.compact.map(&:to_s) success = system(*cmd_args) log.debug "`#{command}` exited with success #{success}" fail ExecutionFailed.new(command, $CHILD_STATUS.exitstatus, nil, nil) unless success end |