def exec(command, options = {}, &block)
      raise Shells::NotRunning unless running?

      options ||= {}
      options = { timeout_error: true, get_output: true }.merge(options)
      options = self.options.merge(options.inject({}) { |m,(k,v)| m[k.to_sym] = v; m })
      options[:retrieve_exit_code] = self.options[:retrieve_exit_code] if options[:retrieve_exit_code] == :default
      options[:on_non_zero_exit_code] = self.options[:on_non_zero_exit_code] unless [:raise, :ignore].include?(options[:on_non_zero_exit_code])
      options[:silence_timeout] = self.options[:silence_timeout] if options[:silence_timeout] == :default
      options[:command_timeout] = self.options[:command_timeout] if options[:command_timeout] == :default
      options[:command_is_echoed] = true if options[:command_is_echoed].nil?
      ret = ''

      merge_local_buffer do
        begin
          # buffer while also passing data to the supplied block.
          if block_given?
            buffer_output(&block)
          end

          command = command.to_s

          # send the command and wait for the prompt to return.
          debug 'Queueing command: ' + command
          queue_input command + line_ending
          if wait_for_prompt(options[:silence_timeout], options[:command_timeout], options[:timeout_error])
            # get the output of the command, minus the trailing prompt.
            ret =
                if options[:get_output]
                  debug 'Reading output of command...'
                  command_output command, options[:command_is_echoed]
                else
                  ''
                end

            if options[:retrieve_exit_code]
              self.last_exit_code = get_exit_code
              if options[:on_non_zero_exit_code] == :raise
                raise NonZeroExitCode.new(last_exit_code) unless last_exit_code == 0 || last_exit_code == :undefined
              end
            else
              self.last_exit_code = nil
            end
          else
            # A timeout occurred and timeout_error was set to false.
            self.last_exit_code = :timeout
            ret = output
          end

        ensure
          # return buffering to normal.
          if block_given?
            buffer_output
          end

        end
      end

      ret
    end
def exec(command, options = {}, &block)
      raise Shells::NotRunning unless running?

      options ||= {}
      options = { timeout_error: true, get_output: true }.merge(options)
      options = self.options.merge(options.inject({}) { |m,(k,v)| m[k.to_sym] = v; m })
      options[:retrieve_exit_code] = self.options[:retrieve_exit_code] if options[:retrieve_exit_code] == :default
      options[:on_non_zero_exit_code] = self.options[:on_non_zero_exit_code] unless [:raise, :ignore].include?(options[:on_non_zero_exit_code])
      options[:silence_timeout] = self.options[:silence_timeout] if options[:silence_timeout] == :default
      options[:command_timeout] = self.options[:command_timeout] if options[:command_timeout] == :default
      options[:command_is_echoed] = true if options[:command_is_echoed].nil?
      ret = ''

      merge_local_buffer do
        begin
          # buffer while also passing data to the supplied block.
          if block_given?
            buffer_output(&block)
          end

          command = command.to_s

          # send the command and wait for the prompt to return.
          debug 'Queueing command: ' + command
          queue_input command + line_ending
          if wait_for_prompt(options[:silence_timeout], options[:command_timeout], options[:timeout_error])
            # get the output of the command, minus the trailing prompt.
            ret =
                if options[:get_output]
                  debug 'Reading output of command...'
                  command_output command, options[:command_is_echoed]
                else
                  ''
                end

            if options[:retrieve_exit_code]
              self.last_exit_code = get_exit_code
              if options[:on_non_zero_exit_code] == :raise
                raise NonZeroExitCode.new(last_exit_code) unless last_exit_code == 0 || last_exit_code == :undefined
              end
            else
              self.last_exit_code = nil
            end
          else
            # A timeout occurred and timeout_error was set to false.
            self.last_exit_code = :timeout
            ret = output
          end

        ensure
          # return buffering to normal.
          if block_given?
            buffer_output
          end

        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+ can be used to override the exit code behavior.
 In all cases, the :default option is the same as not providing the option and will cause +exec+
 to inherit the option from the shell's options.

 +retrieve_exit_code+::
       This can be one of :default, true, or false.
 +on_non_zero_exit_code+::
       This can be on ot :default, :ignore, or :raise.
 +silence_timeout+::
       This can be :default or the number of seconds to wait in silence before timing out.
 +command_timeout+::
       This can be :default or the maximum number of seconds to wait for a command to finish before timing out.

 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