Last active
September 3, 2022 13:42
-
-
Save nsmmrs/4e73b23850ed884527ddc3e01da4a066 to your computer and use it in GitHub Desktop.
A convenience wrapper around `Open3.capture3` for easily running commands, capturing the output, and handling failures.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "open3" | |
require "json" | |
class ShellCommand | |
attr_reader :line, :stdin, :binmode, :stdout, :stderr, :status | |
def initialize(line, stdin, binmode, stdout, stderr, status) | |
@line = line | |
@stdin = stdin | |
@binmode = binmode | |
@stdout = stdout | |
@stderr = stderr | |
@status = status | |
end | |
private_class_method :new | |
def success?() status.success? end | |
def pid() status.pid end | |
def exit_code() status.exitstatus end | |
def self.run(*line, **options) | |
line = line.map(&:to_s) | |
stdin = options[:stdin_data] || "" | |
binmode = options[:binmode] || false | |
args = line + [options] | |
captures = begin | |
Open3.capture3(*args) | |
rescue | |
raise Invalid.new(args) | |
end | |
command = new(line, stdin, binmode, *captures) | |
case | |
when block_given? | |
yield command | |
when command.success? | |
command.stdout.rstrip | |
else | |
raise Failed.new(command) | |
end | |
end | |
class Failed < StandardError | |
attr_reader :command | |
def initialize(command) | |
@command = command | |
end | |
def message | |
"Shell command failed: #{command.to_json}" | |
end | |
end | |
class Invalid < StandardError | |
def initialize(line) | |
super "Shell command invalid (did not run): #{line.to_json}" | |
end | |
end | |
def as_json | |
{ | |
line: line, | |
stdin: stdin, | |
binmode: binmode, | |
stdout: stdout, | |
stderr: stderr, | |
pid: pid, | |
exit_code: exit_code, | |
} | |
end | |
def to_json(*args) | |
as_json.to_json(*args) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wrote a simpler version of this to clean up a bunch of code around
system()
calls in one of our Rails apps.I liked how it turned out, so I cleaned it up and fleshed it out a little.
ShellCommand.run
takes the same arguments asOpen3.capture3
, but it simply returns the output of a command, or raises an error with all the command info (both as an object and in the message).You can also pass a block to do your own error handling, or ignore errors. For example, the following will return
false
rather than raise an exception in the failure case: