class TrickBag::Networking::SshOutputReader
Runs an ssh command, collecting the stdout and stderr produced by it. Optionally a predicate can be specified that, when called with the instance of this class, returns true to close the channel or false to continue.
There are instance methods to return the most recent and all accumulated text for both stdout and stderr, so the predicate can easily inspect the text.
While the predicate will most likely be a lambda, it can be any object that responds to call().
Attributes
Public Class Methods
# File lib/trick_bag/networking/ssh_output_reader.rb, line 22 def initialize(host, command, user = ENV['USER'], password = nil) @host = host @user = user @password = password @command = command @exit_predicate = ->(instance_of_this_class) { false } clear_buffers end
Public Instance Methods
# File lib/trick_bag/networking/ssh_output_reader.rb, line 32 def clear_buffers @latest_stdout = '' @all_stdout = '' @latest_stderr = '' @all_stderr = '' end
The channel will automatically be closed if the predicate returns false or if the command completes. This method is provided for cases where the predicate wants to raise an error; close should be called before raising the error.
# File lib/trick_bag/networking/ssh_output_reader.rb, line 108 def close channel.close if channel end
Runs the specified command. If an exit predicate has been specified via set_exit_predicate
, then it will be called whenever anything is output to stdout or stderr, and the channel will be closed if the predicate returns true.
@return self, to support chaining
# File lib/trick_bag/networking/ssh_output_reader.rb, line 66 def run clear_buffers Net::SSH.start(host, user, password: password) do |ssh| # Open a new channel and configure a minimal set of callbacks, then run # the event loop until the channel finishes (closes). self.channel = ssh.open_channel do |ch| ch.exec(command) do |ch, success| raise "could not execute command" unless success # "on_data" is called when the process writes something to stdout ch.on_data do |c, data| self.latest_output_type = :stdout self.latest_stdout = data self.all_stdout << data $stdout.print data ch.close if exit_predicate.(self) end # "on_extended_data" is called when the process writes something to stderr ch.on_extended_data do |c, type, data| self.latest_output_type = :stderr self.latest_stderr = data self.all_stderr << data $stderr.print data ch.close if exit_predicate.(self) end ch.on_close { self.channel = nil; return self } end end channel.wait self.channel = nil self end end
Sets a predicate that, when called with this reader instance, returns true to close the channel and return, or false to permit the channel to continue operating.
Using this self value, any instance methods can be called; the most useful will probably be latest_stdout
, all_stdout
, latest_stderr
, and all_stderr. There is also a latest_output_type
that returns :stdout or :stderr to indicate which type of output just occurred.
For example, to exit when the string “exit” is sent to stdout: reader.set_exit_predicate(->(reader) { /exit/ === reader.latest_stdout })
Returns self to facilitate chaining, e.g.: stdout, stderr = SshOutputReader.new(...)
.set_exit_predicate(…).run
# File lib/trick_bag/networking/ssh_output_reader.rb, line 54 def set_exit_predicate(exit_predicate) @exit_predicate = exit_predicate self end
# File lib/trick_bag/networking/ssh_output_reader.rb, line 113 def to_s "host = #{host}, command = #{command}, user = #{user}" # password intentionally omitted end