def run
      Open3.popen3(@cmd) do |stdin, stdout, stderr, wait_thr|
        stdin.close # leaving stdin open when we don't use it can cause some commands to hang
        stdout_buffer=""
        stderr_buffer=""

        streams = [stdout, stderr]
        while streams.length > 0
          IO.select(streams).flatten.compact.each do |io|
            if io.eof?
              streams.delete io
              next
            end

            stdout_buffer += io.readpartial(1) if io.fileno == stdout.fileno
            stderr_buffer += io.readpartial(1) if io.fileno == stderr.fileno
          end

          # Remove and process all the finished lines from the output buffer
          stdout_buffer.sub!(/.*\n/m) do
            @out += $&
            if @show_out
              $&.strip.split("\n").each do |line|
                line = @tag.style("cmd") + " " + line if @tag
                log(@out_level, line)
              end
            end

            ''
          end

          # Remove and process all the finished lines from the error buffer
          stderr_buffer.sub!(/.*\n/m) do
            @err += $&
            if @show_err
              $&.strip.split("\n").each do |line|
                line = @tag.style("cmd") + " " + line if @tag
                log(:err, line)
              end
            end

            ''
          end
        end

        @status = wait_thr.value
      end

      if (@expect.is_a?(Array) && !@expect.include?(@status.exitstatus)) ||
         (@expect.is_a?(Integer) && @status.exitstatus != @expect)
        unless @show_err
          err_lines = @err.split "\n"
          err_lines.each do |l|
            l = @tag.style("cmd") + " " + l if @tag
            log(:err, l.chomp)
          end
        end
        raise "'#{@cmd}' failed!" if @raise
      end
    end
def run
      Open3.popen3(@cmd) do |stdin, stdout, stderr, wait_thr|
        stdin.close # leaving stdin open when we don't use it can cause some commands to hang
        stdout_buffer=""
        stderr_buffer=""

        streams = [stdout, stderr]
        while streams.length > 0
          IO.select(streams).flatten.compact.each do |io|
            if io.eof?
              streams.delete io
              next
            end

            stdout_buffer += io.readpartial(1) if io.fileno == stdout.fileno
            stderr_buffer += io.readpartial(1) if io.fileno == stderr.fileno
          end

          # Remove and process all the finished lines from the output buffer
          stdout_buffer.sub!(/.*\n/m) do
            @out += $&
            if @show_out
              $&.strip.split("\n").each do |line|
                line = @tag.style("cmd") + " " + line if @tag
                log(@out_level, line)
              end
            end

            ''
          end

          # Remove and process all the finished lines from the error buffer
          stderr_buffer.sub!(/.*\n/m) do
            @err += $&
            if @show_err
              $&.strip.split("\n").each do |line|
                line = @tag.style("cmd") + " " + line if @tag
                log(:err, line)
              end
            end

            ''
          end
        end

        @status = wait_thr.value
      end

      if (@expect.is_a?(Array) && !@expect.include?(@status.exitstatus)) ||
         (@expect.is_a?(Integer) && @status.exitstatus != @expect)
        unless @show_err
          err_lines = @err.split "\n"
          err_lines.each do |l|
            l = @tag.style("cmd") + " " + l if @tag
            log(:err, l.chomp)
          end
        end
        raise "'#{@cmd}' failed!" if @raise
      end
    end
Execute the command and collect all the data from it.

 The function will block until the command has finished.