From 0098d1d2f9b21349a8d0dca7325d040daa0d0781 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Sun, 24 May 2020 09:27:17 +0200 Subject: [PATCH 1/2] cmd: allow custom line split Signed-off-by: Yoan Blanc --- cmd.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cmd.go b/cmd.go index 29bd9e2..2235bcf 100644 --- a/cmd.go +++ b/cmd.go @@ -126,6 +126,10 @@ type Options struct { // faster and more efficient than polling Cmd.Status. The caller must read both // streaming channels, else lines are dropped silently. Streaming bool + + // If Streaming is true and SplitChar is set, Cmd.Stdout and Cmd.Stderr + // channels will be split using this char instead of the default '\n'. + SplitChar *byte } // NewCmdOptions creates a new Cmd with options. The command is not started @@ -157,6 +161,11 @@ func NewCmdOptions(options Options, name string, args ...string) *Cmd { c.Stderr = make(chan string, DEFAULT_STREAM_CHAN_SIZE) c.stderrStream = NewOutputStream(c.Stderr) + + if options.SplitChar != nil { + c.stdoutStream.splitChar = *options.SplitChar + c.stderrStream.splitChar = *options.SplitChar + } } return c @@ -544,6 +553,7 @@ type OutputStream struct { bufSize int buf []byte lastChar int + splitChar byte } // NewOutputStream creates a new streaming output on the given channel. The @@ -553,9 +563,10 @@ func NewOutputStream(streamChan chan string) *OutputStream { out := &OutputStream{ streamChan: streamChan, // -- - bufSize: DEFAULT_LINE_BUFFER_SIZE, - buf: make([]byte, DEFAULT_LINE_BUFFER_SIZE), - lastChar: 0, + bufSize: DEFAULT_LINE_BUFFER_SIZE, + buf: make([]byte, DEFAULT_LINE_BUFFER_SIZE), + lastChar: 0, + splitChar: '\n', } return out } @@ -571,7 +582,7 @@ func (rw *OutputStream) Write(p []byte) (n int, err error) { // can contain multiple lines, like "foo\nbar". So in that case nextLine // will be 0 ("foo\nbar\n") then 4 ("bar\n") on next iteration. And i // will be 3 and 7, respectively. So lines are [0:3] are [4:7]. - newlineOffset := bytes.IndexByte(p[firstChar:], '\n') + newlineOffset := bytes.IndexByte(p[firstChar:], rw.splitChar) if newlineOffset < 0 { break // no newline in stream, next line incomplete } From f3728cce9334595c44f2b0ada215296cd7a55e85 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Sun, 24 May 2020 09:52:48 +0200 Subject: [PATCH 2/2] flush before closing Signed-off-by: Yoan Blanc --- cmd.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd.go b/cmd.go index 2235bcf..1c95dc1 100644 --- a/cmd.go +++ b/cmd.go @@ -335,6 +335,8 @@ func (c *Cmd) run() { // who's waiting for us to close them. if c.stdoutStream != nil { defer func() { + c.stdoutStream.Flush() + c.stderrStream.Flush() // exec.Cmd.Wait has already waited for all output: // Otherwise, during the execution of the command a separate goroutine // reads from the process over a pipe and delivers that data to the @@ -646,3 +648,11 @@ func (rw *OutputStream) SetLineBufferSize(n int) { rw.bufSize = n rw.buf = make([]byte, rw.bufSize) } + +// Flush empties the buffer of its last line. +func (rw *OutputStream) Flush() { + if rw.lastChar > 0 { + line := string(rw.buf[0:rw.lastChar]) + rw.streamChan <- line + } +}