class Scriptster::ShellCmd
Represent an executed shell command.
The command will be executed in the constructor. It runs in the foreground, so your application will block until it's finished executing. The logs, however, will be printed real-time as the command prints its output.
@attr [Process::status] status The exit status of the command. @attr [String] out The content of the STDOUT of the command. @attr [String] err The content of the STDERR of the command.
Attributes
Public Class Methods
Initialise the object and run the command
@param [String] cmd The command line to be run. @param [Hash] opts Various options of the command. @option opts [Boolean] :show_out Care about STDOUT flag. @option opts [Boolean] :out_level To which log level to print the output
[default: :info].
@option opts [Boolean] :show_err Care about STDERR flag. @option opts [Boolean] :raise Raise on error flag. @option opts [String] :tag Logger
tag (defaults to the first
word of the command line).
@option opts [Integer, Array<Integer>] :expect Expected return values.
# File lib/scriptster/shellcmd.rb, line 56 def initialize(cmd, opts={}) @out = "" @err = "" @show_out = false @out_level = :info @show_err = true @raise = true @tag = cmd.split[0] @expect = 0 opts.each do |k, v| self.instance_variable_set("@#{k.to_s}", v) end @cmd = cmd @status = nil run end
Private Instance Methods
Execute the command and collect all the data from it.
The function will block until the command has finished.
# File lib/scriptster/shellcmd.rb, line 81 def run Open3.popen3(@cmd) do |stdin, stdout, stderr, wait_thr| stdin.close # leaving stdin open when we don't use it can cause some commands to hang stdout_buffer="" stderr_buffer="" streams = [stdout, stderr] while streams.length > 0 IO.select(streams).flatten.compact.each do |io| if io.eof? streams.delete io next end stdout_buffer += io.readpartial(1) if io.fileno == stdout.fileno stderr_buffer += io.readpartial(1) if io.fileno == stderr.fileno end # Remove and process all the finished lines from the output buffer stdout_buffer.sub!(/.*\n/m) do @out += $& if @show_out $&.strip.split("\n").each do |line| line = @tag.style("cmd") + " " + line if @tag log(@out_level, line) end end '' end # Remove and process all the finished lines from the error buffer stderr_buffer.sub!(/.*\n/m) do @err += $& if @show_err $&.strip.split("\n").each do |line| line = @tag.style("cmd") + " " + line if @tag log(:err, line) end end '' end end @status = wait_thr.value end if (@expect.is_a?(Array) && !@expect.include?(@status.exitstatus)) || (@expect.is_a?(Integer) && @status.exitstatus != @expect) unless @show_err err_lines = @err.split "\n" err_lines.each do |l| l = @tag.style("cmd") + " " + l if @tag log(:err, l.chomp) end end raise "'#{@cmd}' failed!" if @raise end end