func (e *Executor) Err() io.ReadCloser {
	return e.stderr
}
Err returns an io.ReadCloser which is the stream of the standard error stream.
package executor
import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os/exec"
	"syscall"
	"time"
	"golang.org/x/net/context"
	"github.com/sirupsen/logrus"
)
type ExecResult struct {
	Stdout     string
	Stderr     string
	ExitStatus int
	Runtime    time.Duration
	executor *Executor
}
type Executor struct {
	LogInterval time.Duration
	LogFunc func(string, ...interface{})
	Stdin io.Reader
	io              bool
	capture         bool
	command         *exec.Cmd
	stdout          io.ReadCloser
	stderr          io.ReadCloser
	stdoutBuf       bytes.Buffer
	stderrBuf       bytes.Buffer
	startTime       time.Time
	terminateLogger chan struct{}
}
func New(cmd *exec.Cmd) *Executor {
	return newExecutor(false, false, cmd)
}
func NewIO(cmd *exec.Cmd) *Executor {
	return newExecutor(true, false, cmd)
}
func NewCapture(cmd *exec.Cmd) *Executor {
	return newExecutor(true, true, cmd)
}
func newExecutor(useIO, useCapture bool, cmd *exec.Cmd) *Executor {
	return &Executor{
		io:              useIO,
		capture:         useCapture,
		LogInterval:     1 * time.Minute,
		LogFunc:         logrus.Debugf,
		command:         cmd,
		stdout:          nil,
		stderr:          nil,
		terminateLogger: make(chan struct{}),
	}
}
func (e *Executor) String() string {
	return fmt.Sprintf("%v (%v) (pid: %v)", e.command.Args, e.command.Path, e.PID())
}
func (e *Executor) Start() error {
	e.command.Stdin = e.Stdin
	e.startTime = time.Now()
	var err error
	if e.io {
		e.stdout, err = e.command.StdoutPipe()
		if err != nil {
			return err
		}
		e.stderr, err = e.command.StderrPipe()
		if err != nil {
			return err
		}
	}
	if err := e.command.Start(); err != nil {
		e.LogFunc("Error executing %v: %v", e, err)
		return err
	}
	if e.LogInterval > 0 {
		go e.logInterval()
	}
	return nil
}
func (e *Executor) TimeRunning() time.Duration {
	return time.Now().Sub(e.startTime)
}
func (e *Executor) logInterval() {
	for {
		select {
		case <-e.terminateLogger:
			return
		case <-time.After(e.LogInterval):
			e.LogFunc("%v has been running for %v", e, e.TimeRunning())
		}
	}
}
func (e *Executor) PID() uint32 {
	if e.command.Process != nil {
		return uint32(e.command.Process.Pid)
	}
	return 0
}
func (e *Executor) Wait(ctx context.Context) (*ExecResult, error) {
	defer close(e.terminateLogger)
	var err error
	errChan := make(chan error, 1)
	go func() {
		if e.capture {
			if _, err := bufio.NewReader(e.stdout).WriteTo(&e.stdoutBuf); err != nil {
				e.LogFunc("error reading stdout: %v", err)
			}
			if _, err := bufio.NewReader(e.stderr).WriteTo(&e.stderrBuf); err != nil {
				e.LogFunc("error reading stderr: %v", err)
			}
		}
		errChan <- e.command.Wait()
	}()
	select {
	case <-ctx.Done():
		if e.command.Process == nil {
			e.LogFunc("Could not terminate non-running command %v", e)
		} else {
			e.LogFunc("Command %v terminated due to timeout or cancellation. It may not have finished!", e)
			e.command.Process.Kill()
		}
		err = ctx.Err()
	case err = <-errChan:
	}
	res := &ExecResult{executor: e}
	if err != nil {
		if exit, ok := err.(*exec.ExitError); ok {
			res.ExitStatus = int(exit.ProcessState.Sys().(syscall.WaitStatus) / 256)
		}
	}
	if e.capture {
		res.Stdout = e.stdoutBuf.String()
		res.Stderr = e.stderrBuf.String()
	}
	res.Runtime = e.TimeRunning()
	return res, err
}
func (e *Executor) Run(ctx context.Context) (*ExecResult, error) {
	if err := e.Start(); err != nil {
		return nil, err
	}
	er, err := e.Wait(ctx)
	if err != nil {
		return er, err
	}
	return er, nil
}
func (e *Executor) Out() io.ReadCloser {
	return e.stdout
}