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 }