55//
66// A basic example that runs env and prints its output:
77//
8- // import (
9- // "fmt"
10- // "github.com/go-cmd/cmd"
11- // )
8+ // import (
9+ // "fmt"
10+ // "github.com/go-cmd/cmd"
11+ // )
1212//
13- // func main() {
14- // // Create Cmd, buffered output
15- // envCmd := cmd.NewCmd("env")
13+ // func main() {
14+ // // Create Cmd, buffered output
15+ // envCmd := cmd.NewCmd("env")
1616//
17- // // Run and wait for Cmd to return Status
18- // status := <-envCmd.Start()
17+ // // Run and wait for Cmd to return Status
18+ // status := <-envCmd.Start()
1919//
20- // // Print each line of STDOUT from Cmd
21- // for _, line := range status.Stdout {
22- // fmt.Println(line)
23- // }
24- // }
20+ // // Print each line of STDOUT from Cmd
21+ // for _, line := range status.Stdout {
22+ // fmt.Println(line)
23+ // }
24+ // }
2525//
2626// Commands can be ran synchronously (blocking) or asynchronously (non-blocking):
2727//
28- // envCmd := cmd.NewCmd("env") // create
28+ // envCmd := cmd.NewCmd("env") // create
2929//
30- // status := <-envCmd.Start() // run blocking
30+ // status := <-envCmd.Start() // run blocking
3131//
32- // statusChan := envCmd.Start() // run non-blocking
33- // // Do other work while Cmd is running...
34- // status <- statusChan // blocking
32+ // statusChan := envCmd.Start() // run non-blocking
33+ // // Do other work while Cmd is running...
34+ // status <- statusChan // blocking
3535//
3636// Start returns a channel to which the final Status is sent when the command
3737// finishes for any reason. The first example blocks receiving on the channel.
@@ -111,9 +111,9 @@ var (
111111// for any reason, this combination of values indicates success (presuming the
112112// command only exits zero on success):
113113//
114- // Exit = 0
115- // Error = nil
116- // Complete = true
114+ // Exit = 0
115+ // Error = nil
116+ // Complete = true
117117//
118118// Error is a Go error from the underlying os/exec.Cmd.Start or os/exec.Cmd.Wait.
119119// If not nil, the command either failed to start (it never ran) or it started
@@ -147,6 +147,12 @@ type Options struct {
147147 // See Cmd.Status for more info.
148148 Buffered bool
149149
150+ // If CombinedOutput is true, STDOUT and STDERR are written to Status.Stdout ONLY similar to 2>&1.
151+ // If CombinedOutput is used at the same time as Buffered, CombinedOutput will take preference.
152+ // Status.StdErr will be empty. The caller can call Cmd.Status to read output at intervals.
153+ // See Cmd.Status for more info.
154+ CombinedOutput bool
155+
150156 // If Streaming is true, Cmd.Stdout and Cmd.Stderr channels are created and
151157 // STDOUT and STDERR output lines are written them in real time. This is
152158 // faster and more efficient than polling Cmd.Status. The caller must read both
@@ -193,6 +199,11 @@ func NewCmdOptions(options Options, name string, args ...string) *Cmd {
193199 c .stderrBuf = NewOutputBuffer ()
194200 }
195201
202+ if options .CombinedOutput {
203+ c .stdoutBuf = NewOutputBuffer ()
204+ c .stderrBuf = nil
205+ }
206+
196207 if options .Streaming {
197208 c .Stdout = make (chan string , DEFAULT_STREAM_CHAN_SIZE )
198209 c .stdoutStream = NewOutputStream (c .Stdout )
@@ -223,8 +234,9 @@ func NewCmdOptions(options Options, name string, args ...string) *Cmd {
223234func (c * Cmd ) Clone () * Cmd {
224235 clone := NewCmdOptions (
225236 Options {
226- Buffered : c .stdoutBuf != nil ,
227- Streaming : c .stdoutStream != nil ,
237+ Buffered : c .stdoutBuf != nil ,
238+ CombinedOutput : c .stdoutBuf != nil ,
239+ Streaming : c .stdoutStream != nil ,
228240 },
229241 c .Name ,
230242 c .Args ... ,
@@ -246,13 +258,13 @@ func (c *Cmd) Clone() *Cmd {
246258// can use to receive the final Status of the command when it ends. The caller
247259// can start the command and wait like,
248260//
249- // status := <-myCmd.Start() // blocking
261+ // status := <-myCmd.Start() // blocking
250262//
251263// or start the command asynchronously and be notified later when it ends,
252264//
253- // statusChan := myCmd.Start() // non-blocking
254- // // Do other work while Cmd is running...
255- // status := <-statusChan // blocking
265+ // statusChan := myCmd.Start() // non-blocking
266+ // // Do other work while Cmd is running...
267+ // status := <-statusChan // blocking
256268//
257269// Exactly one Status is sent on the channel when the command ends. The channel
258270// is not closed. Any Go error is set to Status.Error. Start is idempotent; it
@@ -320,9 +332,9 @@ func (c *Cmd) Stop() error {
320332// as of the Status call time. For example, if the command counts to 3 and three
321333// calls are made between counts, Status.Stdout contains:
322334//
323- // "1"
324- // "1 2"
325- // "1 2 3"
335+ // "1"
336+ // "1 2"
337+ // "1 2 3"
326338//
327339// The caller is responsible for tailing the buffered output if needed. Else,
328340// consider using streaming output. When the command finishes, buffered output
@@ -344,9 +356,12 @@ func (c *Cmd) Status() Status {
344356 if ! c .final {
345357 if c .stdoutBuf != nil {
346358 c .status .Stdout = c .stdoutBuf .Lines ()
347- c .status .Stderr = c .stderrBuf .Lines ()
348359 c .stdoutBuf = nil // release buffers
349- c .stderrBuf = nil
360+
361+ }
362+ if c .stderrBuf != nil {
363+ c .status .Stderr = c .stderrBuf .Lines ()
364+ c .stderrBuf = nil // release buffers
350365 }
351366 c .final = true
352367 }
@@ -355,6 +370,9 @@ func (c *Cmd) Status() Status {
355370 c .status .Runtime = time .Now ().Sub (c .startTime ).Seconds ()
356371 if c .stdoutBuf != nil {
357372 c .status .Stdout = c .stdoutBuf .Lines ()
373+
374+ }
375+ if c .stderrBuf != nil {
358376 c .status .Stderr = c .stderrBuf .Lines ()
359377 }
360378 }
@@ -391,12 +409,19 @@ func (c *Cmd) run(in io.Reader) {
391409 // Set exec.Cmd.Stdout and .Stderr to our concurrent-safe stdout/stderr
392410 // buffer, stream both, or neither
393411 switch {
394- case c .stdoutBuf != nil && c .stdoutStream != nil : // buffer and stream
412+
413+ case c .stdoutBuf != nil && c .stderrBuf != nil && c .stdoutStream != nil : // buffer and stream
395414 cmd .Stdout = io .MultiWriter (c .stdoutStream , c .stdoutBuf )
396415 cmd .Stderr = io .MultiWriter (c .stderrStream , c .stderrBuf )
397- case c .stdoutBuf != nil : // buffer only
416+ case c .stdoutBuf != nil && c .stderrBuf == nil && c .stdoutStream != nil : // combined buffer and stream
417+ cmd .Stdout = io .MultiWriter (c .stdoutStream , c .stdoutBuf )
418+ cmd .Stderr = io .MultiWriter (c .stderrStream , c .stdoutBuf )
419+ case c .stdoutBuf != nil && c .stderrBuf != nil : // buffer only
398420 cmd .Stdout = c .stdoutBuf
399421 cmd .Stderr = c .stderrBuf
422+ case c .stdoutBuf != nil && c .stderrBuf == nil : // buffer combining stderr into stdout
423+ cmd .Stdout = c .stdoutBuf
424+ cmd .Stderr = c .stdoutBuf
400425 case c .stdoutStream != nil : // stream only
401426 cmd .Stdout = c .stdoutStream
402427 cmd .Stderr = c .stderrStream
@@ -521,11 +546,11 @@ func (c *Cmd) run(in io.Reader) {
521546// default when created by calling NewCmd. To use OutputBuffer directly with
522547// a Go standard library os/exec.Command:
523548//
524- // import "os/exec"
525- // import "github.com/go-cmd/cmd"
526- // runnableCmd := exec.Command(...)
527- // stdout := cmd.NewOutputBuffer()
528- // runnableCmd.Stdout = stdout
549+ // import "os/exec"
550+ // import "github.com/go-cmd/cmd"
551+ // runnableCmd := exec.Command(...)
552+ // stdout := cmd.NewOutputBuffer()
553+ // runnableCmd.Stdout = stdout
529554//
530555// While runnableCmd is running, call stdout.Lines() to read all output
531556// currently written.
@@ -613,20 +638,19 @@ func (e ErrLineBufferOverflow) Error() string {
613638// created by calling NewCmdOptions and Options.Streaming is true. To use
614639// OutputStream directly with a Go standard library os/exec.Command:
615640//
616- // import "os/exec"
617- // import "github.com/go-cmd/cmd"
618- //
619- // stdoutChan := make(chan string, 100)
620- // go func() {
621- // for line := range stdoutChan {
622- // // Do something with the line
623- // }
624- // }()
641+ // import "os/exec"
642+ // import "github.com/go-cmd/cmd"
625643//
626- // runnableCmd := exec.Command(...)
627- // stdout := cmd.NewOutputStream(stdoutChan)
628- // runnableCmd.Stdout = stdout
644+ // stdoutChan := make(chan string, 100)
645+ // go func() {
646+ // for line := range stdoutChan {
647+ // // Do something with the line
648+ // }
649+ // }()
629650//
651+ // runnableCmd := exec.Command(...)
652+ // stdout := cmd.NewOutputStream(stdoutChan)
653+ // runnableCmd.Stdout = stdout
630654//
631655// While runnableCmd is running, lines are sent to the channel as soon as they
632656// are written and newline-terminated by the command.
0 commit comments