Class: Shellout
- Inherits:
-
Object
- Object
- Shellout
- Defined in:
- lib/shellout.rb
Overview
Controls execution of commands delegated to the running shell
Constant Summary collapse
- DEFAULT_EXECUTE_DISPLAY_OUTPUT =
true- DEFAULT_EXECUTE_RETRY_ATTEMPTS =
0- DEFAULT_EXECUTE_RETRY_DELAY_SECS =
2- BLOCK_SIZE =
1024- ShelloutBaseError =
Class.new(StandardError)
- ExecuteCommandFailedError =
Class.new(ShelloutBaseError)
- StreamCommandFailedError =
Class.new(ShelloutBaseError)
Instance Attribute Summary collapse
-
#args ⇒ Object
readonly
Returns the value of attribute args.
-
#env ⇒ Object
readonly
Returns the value of attribute env.
-
#opts ⇒ Object
readonly
Returns the value of attribute opts.
-
#stderr_str ⇒ Object
readonly
Returns the value of attribute stderr_str.
Instance Method Summary collapse
- #command ⇒ Object
- #execute(display_output: true, display_error: true, retry_attempts: DEFAULT_EXECUTE_RETRY_ATTEMPTS, retry_delay_secs: DEFAULT_EXECUTE_RETRY_DELAY_SECS) ⇒ Object
-
#exit_code ⇒ Integer
Exit code from last run command.
-
#initialize(*args, **opts) ⇒ Shellout
constructor
A new instance of Shellout.
- #read_stderr ⇒ Object
- #read_stdout ⇒ Object
- #readlines(limit = -1)) ⇒ Object
- #run ⇒ Object
-
#stream(extra_options = {}) ⇒ Object
Executes the command while printing the output from both stdout and stderr.
-
#success? ⇒ Boolean
Return whether last run command was successful (exit 0).
- #try_run ⇒ Object
Constructor Details
#initialize(*args, **opts) ⇒ Shellout
Returns a new instance of Shellout.
20 21 22 23 24 |
# File 'lib/shellout.rb', line 20 def initialize(*args, **opts) @args = args.flatten @env = opts.delete(:env) || {} @opts = opts end |
Instance Attribute Details
#args ⇒ Object (readonly)
Returns the value of attribute args.
9 10 11 |
# File 'lib/shellout.rb', line 9 def args @args end |
#env ⇒ Object (readonly)
Returns the value of attribute env.
9 10 11 |
# File 'lib/shellout.rb', line 9 def env @env end |
#opts ⇒ Object (readonly)
Returns the value of attribute opts.
9 10 11 |
# File 'lib/shellout.rb', line 9 def opts @opts end |
#stderr_str ⇒ Object (readonly)
Returns the value of attribute stderr_str.
9 10 11 |
# File 'lib/shellout.rb', line 9 def stderr_str @stderr_str end |
Instance Method Details
#command ⇒ Object
26 27 28 |
# File 'lib/shellout.rb', line 26 def command @command ||= args.join(' ') end |
#execute(display_output: true, display_error: true, retry_attempts: DEFAULT_EXECUTE_RETRY_ATTEMPTS, retry_delay_secs: DEFAULT_EXECUTE_RETRY_DELAY_SECS) ⇒ Object
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 |
# File 'lib/shellout.rb', line 30 def execute(display_output: true, display_error: true, retry_attempts: DEFAULT_EXECUTE_RETRY_ATTEMPTS, retry_delay_secs: DEFAULT_EXECUTE_RETRY_DELAY_SECS) retried ||= false GDK::Output.debug("command=[#{command}], opts=[#{opts}], display_output=[#{display_output}], retry_attempts=[#{retry_attempts}]") duration = Benchmark.realtime do display_output ? stream : try_run end GDK::Output.debug("result: success?=[#{success?}], stdout=[#{read_stdout}], stderr=[#{read_stderr}], duration=[#{duration.round(2)} seconds]") raise ExecuteCommandFailedError, command unless success? if retried = "'#{command}' succeeded after retry." GDK::Output.success() end self rescue StreamCommandFailedError, ExecuteCommandFailedError => e = "'#{command}' failed." if (retry_attempts -= 1).negative? GDK::Output.error(, e) if display_error self else retried = true += " Retrying in #{retry_delay_secs} secs.." GDK::Output.error(, e) if display_error sleep(retry_delay_secs) retry end end |
#exit_code ⇒ Integer
Exit code from last run command
135 136 137 138 139 |
# File 'lib/shellout.rb', line 135 def exit_code return nil unless @status @status.exitstatus end |
#read_stderr ⇒ Object
119 120 121 |
# File 'lib/shellout.rb', line 119 def read_stderr clean_string(@stderr_str.to_s.chomp) end |
#read_stdout ⇒ Object
115 116 117 |
# File 'lib/shellout.rb', line 115 def read_stdout clean_string(@stdout_str.to_s.chomp) end |
#readlines(limit = -1)) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/shellout.rb', line 84 def readlines(limit = -1) @stdout_str = '' @stderr_str = '' lines = [] Open3.popen2(env, *args, opts) do |_stdin, stdout, thread| stdout.each_line do |line| lines << line.chomp if limit == -1 || lines.count < limit end thread.join @status = thread.value end @stdout_str = lines.join("\n") lines end |
#run ⇒ Object
103 104 105 106 |
# File 'lib/shellout.rb', line 103 def run capture read_stdout end |
#stream(extra_options = {}) ⇒ Object
Executes the command while printing the output from both stdout and stderr
This command will stream each individual character from a separate thread making it possible to visualize interactive progress bar.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/shellout.rb', line 69 def stream( = {}) @stdout_str = '' @stderr_str = '' # Inspiration: https://nickcharlton.net/posts/ruby-subprocesses-with-stdout-stderr-streams.html Open3.popen3(env, *args, opts.merge()) do |_stdin, stdout, stderr, thread| @status = print_output_from_thread(thread, stdout, stderr) end read_stdout rescue Errno::ENOENT => e print_err(e.) raise StreamCommandFailedError, e end |
#success? ⇒ Boolean
Return whether last run command was successful (exit 0)
126 127 128 129 130 |
# File 'lib/shellout.rb', line 126 def success? return false unless @status @status.success? end |
#try_run ⇒ Object
108 109 110 111 112 113 |
# File 'lib/shellout.rb', line 108 def try_run capture(err: '/dev/null') read_stdout rescue Errno::ENOENT '' end |