github.com/haraldrudell/parl@v0.4.176/pexec/exec.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pexec
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"os/exec"
    12  	"strings"
    13  
    14  	"github.com/haraldrudell/parl/perrors"
    15  )
    16  
    17  const (
    18  	NoStdout   Stdouts = false
    19  	WantStdout Stdouts = true
    20  )
    21  
    22  type Stdouts bool
    23  
    24  const (
    25  	NoStderr Stderrs = iota + 1
    26  	WantStderr
    27  	StderrIsError
    28  )
    29  
    30  type Stderrs uint8
    31  
    32  var NoStdin []byte
    33  
    34  // ExecBlocking executes a system command with independent control over stdin stdout stderr
    35  //   - if stdin is nil, no stdin is provided to command
    36  //   - if wantStdout is true, stdout contains any command output, if false stdout nil
    37  //   - if wantStderr is true, stderr contains any command error output, if false stderr nil
    38  func ExecBlocking(stdin []byte, wantStdout Stdouts, wantStderr Stderrs, ctx context.Context, args ...string) (stdout, stderr *bytes.Buffer, err error) {
    39  	if len(args) == 0 {
    40  		err = perrors.ErrorfPF("%w", ErrArgsListEmpty)
    41  		return
    42  	}
    43  
    44  	var execCmd = exec.CommandContext(ctx, args[0], args[1:]...)
    45  	if stdin != nil {
    46  		execCmd.Stdin = bytes.NewReader(stdin)
    47  	}
    48  	if wantStdout == WantStdout {
    49  		stdout = new(bytes.Buffer)
    50  		execCmd.Stdout = stdout
    51  	}
    52  	var stderr0 bytes.Buffer
    53  	execCmd.Stderr = &stderr0
    54  	if wantStderr == WantStderr {
    55  		stderr = &stderr0
    56  	}
    57  
    58  	// run command
    59  	if err = execCmd.Run(); err != nil {
    60  		err = perrors.ErrorfPF("system command failed: %s error: '%w' stderr: %q",
    61  			strings.Join(args, "\x20"), err, stderr0.String(),
    62  		)
    63  	} else if wantStderr == StderrIsError && stderr0.Len() > 0 {
    64  		err = perrors.ErrorfPF("system command wrote to standard error: %s stderr: %q",
    65  			strings.Join(args, "\x20"), stderr0.String(),
    66  		)
    67  	}
    68  
    69  	return
    70  }