@@ -28,13 +28,14 @@ import (
2828// Pipe represents a pipe object with an associated [ReadAutoCloser].
2929type Pipe struct {
3030 // Reader is the underlying reader.
31- Reader ReadAutoCloser
32- stdout , stderr io.Writer
33- httpClient * http.Client
31+ Reader ReadAutoCloser
32+ stdout io.Writer
33+ httpClient * http.Client
3434
35- // because pipe stages are concurrent, protect 'err'
36- mu * sync.Mutex
37- err error
35+ // because pipe stages are concurrent, protect 'err' and 'stderr'
36+ mu * sync.Mutex
37+ err error
38+ stderr io.Writer
3839}
3940
4041// Args creates a pipe containing the program's command-line arguments from
@@ -414,8 +415,9 @@ func (p *Pipe) Exec(cmdLine string) *Pipe {
414415 cmd .Stdin = r
415416 cmd .Stdout = w
416417 cmd .Stderr = w
417- if p .stderr != nil {
418- cmd .Stderr = p .stderr
418+ pipeStderr := p .stdErr ()
419+ if pipeStderr != nil {
420+ cmd .Stderr = pipeStderr
419421 }
420422 err = cmd .Start ()
421423 if err != nil {
@@ -454,8 +456,9 @@ func (p *Pipe) ExecForEach(cmdLine string) *Pipe {
454456 cmd := exec .Command (args [0 ], args [1 :]... )
455457 cmd .Stdout = w
456458 cmd .Stderr = w
457- if p .stderr != nil {
458- cmd .Stderr = p .stderr
459+ pipeStderr := p .stdErr ()
460+ if pipeStderr != nil {
461+ cmd .Stderr = pipeStderr
459462 }
460463 err = cmd .Start ()
461464 if err != nil {
@@ -839,6 +842,18 @@ func (p *Pipe) Slice() ([]string, error) {
839842 return result , p .Error ()
840843}
841844
845+ // stdErr returns the pipe's configured standard error writer for commands run
846+ // via [Pipe.Exec] and [Pipe.ExecForEach]. The default is nil, which means that
847+ // error output will go to the pipe.
848+ func (p * Pipe ) stdErr () io.Writer {
849+ if p .mu == nil { // uninitialised pipe
850+ return nil
851+ }
852+ p .mu .Lock ()
853+ defer p .mu .Unlock ()
854+ return p .stderr
855+ }
856+ 842857// Stdout copies the pipe's contents to its configured standard output (using
843858// [Pipe.WithStdout]), or to [os.Stdout] otherwise, and returns the number of
844859// bytes successfully written, together with any error.
@@ -913,10 +928,11 @@ func (p *Pipe) WithReader(r io.Reader) *Pipe {
913928 return p
914929}
915930
916- // WithStderr redirects the standard error output for commands run via
917- // [Pipe.Exec] or [Pipe.ExecForEach] to the writer w, instead of going to the
918- // pipe as it normally would.
931+ // WithStderr sets the standard error output for [Pipe.Exec] or
932+ // [Pipe.ExecForEach] commands to w, instead of the pipe.
919933func (p * Pipe ) WithStderr (w io.Writer ) * Pipe {
934+ p .mu .Lock ()
935+ defer p .mu .Unlock ()
920936 p .stderr = w
921937 return p
922938}
0 commit comments