Documentation
¶
Overview ¶
Package cmd runs external commands with concurrent access to output and status. It wraps the Go standard library os/exec.Command to correctly handle reading output (STDOUT and STDERR) while a command is running and killing a command. All operations are safe to call from multiple goroutines.
A simple example that runs env and prints its output:
import (
"fmt"
"github.com/go-cmd/cmd"
)
func main() {
// Create Cmd, buffered output
envCmd := cmd.NewCmd("env")
// Run and wait for Cmd to return Status
status := <-envCmd.Start()
// Print each line of STDOUT from Cmd
for _, line := range status.Stdout {
fmt.Println(line)
}
}
Commands can be ran synchronously (blocking) or asynchronously (non-blocking):
envCmd := cmd.NewCmd("env") // create
status := <-envCmd.Start() // run blocking
statusChan := envCmd.Start() // run non-blocking
// Do other work while Cmd is running...
status <- statusChan // blocking
Start returns a channel, so the first example is blocking because it receives on the channel, but the second example is non-blocking because it saves the channel and receives on it later. The Status function can be called while the Cmd is running. When the Cmd finishes, a final status is sent to the channel returned by Start.
Index ¶
Constants ¶
const ( // DEFAULT_LINE_BUFFER_SIZE is the default size of the OutputStream line buffer. // The default value is usually sufficient, but if ErrLineBufferOverflow errors // occur, try increasing the size by calling OutputBuffer.SetLineBufferSize. DEFAULT_LINE_BUFFER_SIZE = 16384 // DEFAULT_STREAM_CHAN_SIZE is the default string channel size for a Cmd when // Options.Streaming is true. The string channel size can have a minor // performance impact if too small by causing OutputStream.Write to block // excessively. DEFAULT_STREAM_CHAN_SIZE = 1000 )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Cmd ¶
type Cmd struct {
Name string
Args []string
Stdout chan string // streaming STDOUT if enabled, else nil (see Options)
Stderr chan string // streaming STDERR if enabled, else nil (see Options)
*sync.Mutex
// contains filtered or unexported fields
}
Cmd represents an external command, similar to the Go built-in os/exec.Cmd. A Cmd cannot be reused after calling Start. Do not modify exported fields; they are read-only. To create a new Cmd, call NewCmd or NewCmdOptions.
func NewCmd ¶
NewCmd creates a new Cmd for the given command name and arguments. The command is not started until Start is called. Output buffering is on, streaming output is off. To control output, use NewCmdOptions instead.
func NewCmdOptions ¶ added in v1.0.1
NewCmdOptions creates a new Cmd with options. The command is not started until Start is called.
func (*Cmd) Done ¶ added in v1.0.2
func (c *Cmd) Done() <-chan struct{}
Done returns a channel that's closed when the command stops running. Call Status after the command stops to get its final status.
func (*Cmd) Start ¶
Start starts the command and immediately returns a channel that the caller can use to receive the final Status of the command when it ends. The caller can start the command and wait like,
status := <-myCmd.Start() // blocking
or start the command asynchronously and be notified later when it ends,
statusChan := myCmd.Start() // non-blocking // Do other work while Cmd is running... status := <-statusChan // blocking
Exactly one Status is sent on the channel when the command ends. The channel is not closed. Any error is set to Status.Error. Start is idempotent; it always returns the same channel.
type ErrLineBufferOverflow ¶ added in v1.0.1
type ErrLineBufferOverflow struct {
Line string // Unterminated line that caused the error
BufferSize int // Internal line buffer size
BufferFree int // Free bytes in line buffer
}
ErrLineBufferOverflow is returned by OutputStream.Write when the internal line buffer is filled before a newline character is written to terminate a line. Increasing the line buffer size by calling OutputStream.SetLineBufferSize can help prevent this error.
func (ErrLineBufferOverflow) Error ¶ added in v1.0.1
func (e ErrLineBufferOverflow) Error() string
type Options ¶ added in v1.0.1
type Options struct {
// If Buffered is true, STDOUT and STDERR are written to Status.Stdout and
// Status.Stderr. The caller can call Cmd.Status to read output at intervals.
Buffered bool
// If Streaming is true, Cmd.Stdout and Cmd.Stderr channels are created and
// STDOUT and STDERR output lines are written them in real time. This is
// faster and more efficient than polling Cmd.Status. The caller must read both
// streaming channels, else lines are dropped silently.
Streaming bool
}
Options represents customizations for NewCmdOptions.
type OutputBuffer ¶ added in v1.0.1
OutputBuffer represents command output that is saved, line by line, in an unbounded buffer. It is safe for multiple goroutines to read while the command is running and after it has finished. If output is small (a few megabytes) and not read frequently, an output buffer is a good solution.
A Cmd in this package uses an OutputBuffer for both STDOUT and STDERR by default when created by calling NewCmd. To use OutputBuffer directly with a Go standard library os/exec.Command:
import "os/exec" import "github.com/go-cmd/cmd" runnableCmd := exec.Command(...) stdout := cmd.NewOutputBuffer() runnableCmd.Stdout = stdout
While runnableCmd is running, call stdout.Lines() to read all output currently written.
func NewOutputBuffer ¶ added in v1.0.1
func NewOutputBuffer() *OutputBuffer
NewOutputBuffer creates a new output buffer. The buffer is unbounded and safe for multiple goroutines to read while the command is running by calling Lines.
func (*OutputBuffer) Lines ¶ added in v1.0.1
func (rw *OutputBuffer) Lines() []string
Lines returns lines of output written by the Cmd. It is safe to call while the Cmd is running and after it has finished. Subsequent calls returns more lines, if more lines were written. "\r\n" are stripped from the lines.
type OutputStream ¶ added in v1.0.1
type OutputStream struct {
// contains filtered or unexported fields
}
OutputStream represents real time, line by line output from a running Cmd. Lines are terminated by a single newline preceded by an optional carriage return. Both newline and carriage return are stripped from the line when sent to a caller-provided channel.
The caller must begin receiving before starting the Cmd. Write blocks on the channel; the caller must always read the channel. The channel is not closed by the OutputStream.
A Cmd in this package uses an OutputStream for both STDOUT and STDERR when created by calling NewCmdOptions and Options.Streaming is true. To use OutputStream directly with a Go standard library os/exec.Command:
import "os/exec"
import "github.com/go-cmd/cmd"
stdoutChan := make(chan string, 100)
go func() {
for line := range stdoutChan {
// Do something with the line
}
}()
runnableCmd := exec.Command(...)
stdout := cmd.NewOutputStream(stdoutChan)
runnableCmd.Stdout = stdout
While runnableCmd is running, lines are sent to the channel as soon as they are written and newline-terminated by the command. After the command finishes, the caller should wait for the last lines to be sent:
for len(stdoutChan) > 0 {
time.Sleep(10 * time.Millisecond)
}
Since the channel is not closed by the OutputStream, the two indications that all lines have been sent and received are the command finishing and the channel size being zero.
func NewOutputStream ¶ added in v1.0.1
func NewOutputStream(streamChan chan string) *OutputStream
NewOutputStream creates a new streaming output on the given channel. The caller must begin receiving on the channel before the command is started. The OutputStream never closes the channel.
func (*OutputStream) Lines ¶ added in v1.0.1
func (rw *OutputStream) Lines() <-chan string
Lines returns the channel to which lines are sent. This is the same channel passed to NewOutputStream.
func (*OutputStream) SetLineBufferSize ¶ added in v1.0.1
func (rw *OutputStream) SetLineBufferSize(n int)
SetLineBufferSize sets the internal line buffer size. The default is DEFAULT_LINE_BUFFER_SIZE. This function must be called immediately after NewOutputStream, and it is not safe to call by multiple goroutines.
Increasing the line buffer size can help reduce ErrLineBufferOverflow errors.
type Status ¶
type Status struct {
Cmd string
PID int
Complete bool // false if stopped or signaled
Exit int // exit code of process
Error error // Go error
StartTs int64 // Unix ts (nanoseconds)
StopTs int64 // Unix ts (nanoseconds)
Runtime float64 // seconds
Stdout []string // buffered STDOUT
Stderr []string // buffered STDERR
}
Status represents the status of a Cmd. It is valid during the entire lifecycle of the command. If StartTs > 0 (or PID > 0), the command has started. If StopTs > 0, the command has stopped. After the command has stopped, Exit = 0 is usually enough to indicate success, but complete success is indicated by:
Exit = 0 Error = nil Complete = true
If Complete is false, the command was stopped or timed out. Error is a Go error related to starting or running the command.