def exec(command, options={}, &block)
      raise ConnectionClosed.new('Connection is closed.') unless @channel

      options = {
          on_non_zero_exit_code: :default
      }.merge(options || {})

      options[:on_non_zero_exit_code] = @options[:on_non_zero_exit_code] if options[:on_non_zero_exit_code] == :default

      push_buffer # store the current buffer and start a fresh buffer

      # buffer while also passing data to the supplied block.
      if block_given?
        buffer_input( &block )
      end

      # send the command and wait for the prompt to return.
      @channel.send_data command + "\n"
      wait_for_prompt

      # return buffering to normal.
      if block_given?
        buffer_input
      end

      # get the output from the command, minus the trailing prompt.
      ret = command_output(command)

      # restore the original buffer and merge the output from the command.
      pop_merge_buffer

      if @options[:retrieve_exit_code]
        # get the exit code for the command.
        push_buffer
        retrieve_command = 'echo $?'
        @channel.send_data retrieve_command + "\n"
        wait_for_prompt
        @last_exit_code = command_output(retrieve_command).strip.to_i
        # restore the original buffer and discard the output from this command.
        pop_discard_buffer

        # if we are expected to raise an error, do so.
        if options[:on_non_zero_exit_code] == :raise_error
          raise NonZeroExitCode.new("Exit code was #{@last_exit_code}.") unless @last_exit_code == 0
        end
      end

      ret
    end
def exec(command, options={}, &block)
      raise ConnectionClosed.new('Connection is closed.') unless @channel

      options = {
          on_non_zero_exit_code: :default
      }.merge(options || {})

      options[:on_non_zero_exit_code] = @options[:on_non_zero_exit_code] if options[:on_non_zero_exit_code] == :default

      push_buffer # store the current buffer and start a fresh buffer

      # buffer while also passing data to the supplied block.
      if block_given?
        buffer_input( &block )
      end

      # send the command and wait for the prompt to return.
      @channel.send_data command + "\n"
      wait_for_prompt

      # return buffering to normal.
      if block_given?
        buffer_input
      end

      # get the output from the command, minus the trailing prompt.
      ret = command_output(command)

      # restore the original buffer and merge the output from the command.
      pop_merge_buffer

      if @options[:retrieve_exit_code]
        # get the exit code for the command.
        push_buffer
        retrieve_command = 'echo $?'
        @channel.send_data retrieve_command + "\n"
        wait_for_prompt
        @last_exit_code = command_output(retrieve_command).strip.to_i
        # restore the original buffer and discard the output from this command.
        pop_discard_buffer

        # if we are expected to raise an error, do so.
        if options[:on_non_zero_exit_code] == :raise_error
          raise NonZeroExitCode.new("Exit code was #{@last_exit_code}.") unless @last_exit_code == 0
        end
      end

      ret
    end
Executes a command during the shell session.

 If called outside of the +new+ block, this will raise an error.

 The +command+ is the command to execute in the shell.

 The +options+ parameter can include the following keys.
 *  The :on_non_zero_exit_code option can be :default, :ignore, or :raise_error.

 If provided, the +block+ is a chunk of code that will be processed every time the
 shell receives output from the program.  If the block returns a string, the string
 will be sent to the shell.  This can be used to monitor processes or monitor and
 interact with processes.  The +block+ is optional.

   shell.exec('sudo -p "password:" nginx restart') do |data,type|
     return 'super-secret' if /password:$/.match(data)
     nil
   end