Source File
exec.go
Belonging Package
os/exec
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package exec runs external commands. It wraps os.StartProcess to make it
// easier to remap stdin and stdout, connect I/O with pipes, and do other
// adjustments.
//
// Unlike the "system" library call from C and other languages, the
// os/exec package intentionally does not invoke the system shell and
// does not expand any glob patterns or handle other expansions,
// pipelines, or redirections typically done by shells. The package
// behaves more like C's "exec" family of functions. To expand glob
// patterns, either call the shell directly, taking care to escape any
// dangerous input, or use the path/filepath package's Glob function.
// To expand environment variables, use package os's ExpandEnv.
//
// Note that the examples in this package assume a Unix system.
// They may not run on Windows, and they do not run in the Go Playground
// used by golang.org and godoc.org.
package exec
import (
)
// Error is returned by LookPath when it fails to classify a file as an
// executable.
type Error struct {
// Name is the file name for which the error occurred.
Name string
// Err is the underlying error.
Err error
}
func ( *Error) () string {
return "exec: " + strconv.Quote(.Name) + ": " + .Err.Error()
}
func ( *Error) () error { return .Err }
// Cmd represents an external command being prepared or run.
//
// A Cmd cannot be reused after calling its Run, Output or CombinedOutput
// methods.
type Cmd struct {
// Path is the path of the command to run.
//
// This is the only field that must be set to a non-zero
// value. If Path is relative, it is evaluated relative
// to Dir.
Path string
// Args holds command line arguments, including the command as Args[0].
// If the Args field is empty or nil, Run uses {Path}.
//
// In typical use, both Path and Args are set by calling Command.
Args []string
// Env specifies the environment of the process.
// Each entry is of the form "key=value".
// If Env is nil, the new process uses the current process's
// environment.
// If Env contains duplicate environment keys, only the last
// value in the slice for each duplicate key is used.
// As a special case on Windows, SYSTEMROOT is always added if
// missing and not explicitly set to the empty string.
Env []string
// Dir specifies the working directory of the command.
// If Dir is the empty string, Run runs the command in the
// calling process's current directory.
Dir string
// Stdin specifies the process's standard input.
//
// If Stdin is nil, the process reads from the null device (os.DevNull).
//
// If Stdin is an *os.File, the process's standard input is connected
// directly to that file.
//
// Otherwise, during the execution of the command a separate
// goroutine reads from Stdin and delivers that data to the command
// over a pipe. In this case, Wait does not complete until the goroutine
// stops copying, either because it has reached the end of Stdin
// (EOF or a read error) or because writing to the pipe returned an error.
Stdin io.Reader
// Stdout and Stderr specify the process's standard output and error.
//
// If either is nil, Run connects the corresponding file descriptor
// to the null device (os.DevNull).
//
// If either is an *os.File, the corresponding output from the process
// is connected directly to that file.
//
// Otherwise, during the execution of the command a separate goroutine
// reads from the process over a pipe and delivers that data to the
// corresponding Writer. In this case, Wait does not complete until the
// goroutine reaches EOF or encounters an error.
//
// If Stdout and Stderr are the same writer, and have a type that can
// be compared with ==, at most one goroutine at a time will call Write.
Stdout io.Writer
Stderr io.Writer
// ExtraFiles specifies additional open files to be inherited by the
// new process. It does not include standard input, standard output, or
// standard error. If non-nil, entry i becomes file descriptor 3+i.
//
// ExtraFiles is not supported on Windows.
ExtraFiles []*os.File
// SysProcAttr holds optional, operating system-specific attributes.
// Run passes it to os.StartProcess as the os.ProcAttr's Sys field.
SysProcAttr *syscall.SysProcAttr
// Process is the underlying process, once started.
Process *os.Process
// ProcessState contains information about an exited process,
// available after a call to Wait or Run.
ProcessState *os.ProcessState
ctx context.Context // nil means none
lookPathErr error // LookPath error, if any.
finished bool // when Wait was called
childFiles []*os.File
closeAfterStart []io.Closer
closeAfterWait []io.Closer
goroutine []func() error
errch chan error // one send per goroutine
waitDone chan struct{}
}
// Command returns the Cmd struct to execute the named program with
// the given arguments.
//
// It sets only the Path and Args in the returned structure.
//
// If name contains no path separators, Command uses LookPath to
// resolve name to a complete path if possible. Otherwise it uses name
// directly as Path.
//
// The returned Cmd's Args field is constructed from the command name
// followed by the elements of arg, so arg should not include the
// command name itself. For example, Command("echo", "hello").
// Args[0] is always name, not the possibly resolved Path.
//
// On Windows, processes receive the whole command line as a single string
// and do their own parsing. Command combines and quotes Args into a command
// line string with an algorithm compatible with applications using
// CommandLineToArgvW (which is the most common way). Notable exceptions are
// msiexec.exe and cmd.exe (and thus, all batch files), which have a different
// unquoting algorithm. In these or other similar cases, you can do the
// quoting yourself and provide the full command line in SysProcAttr.CmdLine,
// leaving Args empty.
func ( string, ...string) *Cmd {
:= &Cmd{
Path: ,
Args: append([]string{}, ...),
}
if filepath.Base() == {
if , := LookPath(); != nil {
.lookPathErr =
} else {
.Path =
}
}
return
}
// CommandContext is like Command but includes a context.
//
// The provided context is used to kill the process (by calling
// os.Process.Kill) if the context becomes done before the command
// completes on its own.
func ( context.Context, string, ...string) *Cmd {
if == nil {
panic("nil Context")
}
:= Command(, ...)
.ctx =
return
}
// String returns a human-readable description of c.
// It is intended only for debugging.
// In particular, it is not suitable for use as input to a shell.
// The output of String may vary across Go releases.
func ( *Cmd) () string {
if .lookPathErr != nil {
// failed to resolve path; report the original requested path (plus args)
return strings.Join(.Args, " ")
}
// report the exact executable path (plus args)
:= new(strings.Builder)
.WriteString(.Path)
for , := range .Args[1:] {
.WriteByte(' ')
.WriteString()
}
return .String()
}
// interfaceEqual protects against panics from doing equality tests on
// two interfaces with non-comparable underlying types.
func (, any) bool {
defer func() {
recover()
}()
return ==
}
func ( *Cmd) () ([]string, error) {
if .Env != nil {
return .Env, nil
}
return execenv.Default(.SysProcAttr)
}
func ( *Cmd) () []string {
if len(.Args) > 0 {
return .Args
}
return []string{.Path}
}
// skipStdinCopyError optionally specifies a function which reports
// whether the provided stdin copy error should be ignored.
var skipStdinCopyError func(error) bool
func ( *Cmd) () ( *os.File, error) {
if .Stdin == nil {
, = os.Open(os.DevNull)
if != nil {
return
}
.closeAfterStart = append(.closeAfterStart, )
return
}
if , := .Stdin.(*os.File); {
return , nil
}
, , := os.Pipe()
if != nil {
return
}
.closeAfterStart = append(.closeAfterStart, )
.closeAfterWait = append(.closeAfterWait, )
.goroutine = append(.goroutine, func() error {
, := io.Copy(, .Stdin)
if := skipStdinCopyError; != nil && () {
= nil
}
if := .Close(); == nil {
=
}
return
})
return , nil
}
func ( *Cmd) () ( *os.File, error) {
return .writerDescriptor(.Stdout)
}
func ( *Cmd) () ( *os.File, error) {
if .Stderr != nil && interfaceEqual(.Stderr, .Stdout) {
return .childFiles[1], nil
}
return .writerDescriptor(.Stderr)
}
func ( *Cmd) ( io.Writer) ( *os.File, error) {
if == nil {
, = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if != nil {
return
}
.closeAfterStart = append(.closeAfterStart, )
return
}
if , := .(*os.File); {
return , nil
}
, , := os.Pipe()
if != nil {
return
}
.closeAfterStart = append(.closeAfterStart, )
.closeAfterWait = append(.closeAfterWait, )
.goroutine = append(.goroutine, func() error {
, := io.Copy(, )
.Close() // in case io.Copy stopped due to write error
return
})
return , nil
}
func ( *Cmd) ( []io.Closer) {
for , := range {
.Close()
}
}
// Run starts the specified command and waits for it to complete.
//
// The returned error is nil if the command runs, has no problems
// copying stdin, stdout, and stderr, and exits with a zero exit
// status.
//
// If the command starts but does not complete successfully, the error is of
// type *ExitError. Other error types may be returned for other situations.
//
// If the calling goroutine has locked the operating system thread
// with runtime.LockOSThread and modified any inheritable OS-level
// thread state (for example, Linux or Plan 9 name spaces), the new
// process will inherit the caller's thread state.
func ( *Cmd) () error {
if := .Start(); != nil {
return
}
return .Wait()
}
// lookExtensions finds windows executable by its dir and path.
// It uses LookPath to try appropriate extensions.
// lookExtensions does not search PATH, instead it converts `prog` into `.\prog`.
func (, string) (string, error) {
if filepath.Base() == {
= filepath.Join(".", )
}
if == "" {
return LookPath()
}
if filepath.VolumeName() != "" {
return LookPath()
}
if len() > 1 && os.IsPathSeparator([0]) {
return LookPath()
}
:= filepath.Join(, )
// We assume that LookPath will only add file extension.
, := LookPath()
if != nil {
return "",
}
:= strings.TrimPrefix(, )
return + , nil
}
// Start starts the specified command but does not wait for it to complete.
//
// If Start returns successfully, the c.Process field will be set.
//
// The Wait method will return the exit code and release associated resources
// once the command exits.
func ( *Cmd) () error {
if .lookPathErr != nil {
.closeDescriptors(.closeAfterStart)
.closeDescriptors(.closeAfterWait)
return .lookPathErr
}
if runtime.GOOS == "windows" {
, := lookExtensions(.Path, .Dir)
if != nil {
.closeDescriptors(.closeAfterStart)
.closeDescriptors(.closeAfterWait)
return
}
.Path =
}
if .Process != nil {
return errors.New("exec: already started")
}
if .ctx != nil {
select {
case <-.ctx.Done():
.closeDescriptors(.closeAfterStart)
.closeDescriptors(.closeAfterWait)
return .ctx.Err()
default:
}
}
.childFiles = make([]*os.File, 0, 3+len(.ExtraFiles))
type func(*Cmd) (*os.File, error)
for , := range []{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
, := ()
if != nil {
.closeDescriptors(.closeAfterStart)
.closeDescriptors(.closeAfterWait)
return
}
.childFiles = append(.childFiles, )
}
.childFiles = append(.childFiles, .ExtraFiles...)
, := .envv()
if != nil {
return
}
.Process, = os.StartProcess(.Path, .argv(), &os.ProcAttr{
Dir: .Dir,
Files: .childFiles,
Env: addCriticalEnv(dedupEnv()),
Sys: .SysProcAttr,
})
if != nil {
.closeDescriptors(.closeAfterStart)
.closeDescriptors(.closeAfterWait)
return
}
.closeDescriptors(.closeAfterStart)
// Don't allocate the channel unless there are goroutines to fire.
if len(.goroutine) > 0 {
.errch = make(chan error, len(.goroutine))
for , := range .goroutine {
go func( func() error) {
.errch <- ()
}()
}
}
if .ctx != nil {
.waitDone = make(chan struct{})
go func() {
select {
case <-.ctx.Done():
.Process.Kill()
case <-.waitDone:
}
}()
}
return nil
}
// An ExitError reports an unsuccessful exit by a command.
type ExitError struct {
*os.ProcessState
// Stderr holds a subset of the standard error output from the
// Cmd.Output method if standard error was not otherwise being
// collected.
//
// If the error output is long, Stderr may contain only a prefix
// and suffix of the output, with the middle replaced with
// text about the number of omitted bytes.
//
// Stderr is provided for debugging, for inclusion in error messages.
// Users with other needs should redirect Cmd.Stderr as needed.
Stderr []byte
}
func ( *ExitError) () string {
return .ProcessState.String()
}
// Wait waits for the command to exit and waits for any copying to
// stdin or copying from stdout or stderr to complete.
//
// The command must have been started by Start.
//
// The returned error is nil if the command runs, has no problems
// copying stdin, stdout, and stderr, and exits with a zero exit
// status.
//
// If the command fails to run or doesn't complete successfully, the
// error is of type *ExitError. Other error types may be
// returned for I/O problems.
//
// If any of c.Stdin, c.Stdout or c.Stderr are not an *os.File, Wait also waits
// for the respective I/O loop copying to or from the process to complete.
//
// Wait releases any resources associated with the Cmd.
func ( *Cmd) () error {
if .Process == nil {
return errors.New("exec: not started")
}
if .finished {
return errors.New("exec: Wait was already called")
}
.finished = true
, := .Process.Wait()
if .waitDone != nil {
close(.waitDone)
}
.ProcessState =
var error
for range .goroutine {
if := <-.errch; != nil && == nil {
=
}
}
.closeDescriptors(.closeAfterWait)
if != nil {
return
} else if !.Success() {
return &ExitError{ProcessState: }
}
return
}
// Output runs the command and returns its standard output.
// Any returned error will usually be of type *ExitError.
// If c.Stderr was nil, Output populates ExitError.Stderr.
func ( *Cmd) () ([]byte, error) {
if .Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
var bytes.Buffer
.Stdout = &
:= .Stderr == nil
if {
.Stderr = &prefixSuffixSaver{N: 32 << 10}
}
:= .Run()
if != nil && {
if , := .(*ExitError); {
.Stderr = .Stderr.(*prefixSuffixSaver).Bytes()
}
}
return .Bytes(),
}
// CombinedOutput runs the command and returns its combined standard
// output and standard error.
func ( *Cmd) () ([]byte, error) {
if .Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
if .Stderr != nil {
return nil, errors.New("exec: Stderr already set")
}
var bytes.Buffer
.Stdout = &
.Stderr = &
:= .Run()
return .Bytes(),
}
// StdinPipe returns a pipe that will be connected to the command's
// standard input when the command starts.
// The pipe will be closed automatically after Wait sees the command exit.
// A caller need only call Close to force the pipe to close sooner.
// For example, if the command being run will not exit until standard input
// is closed, the caller must close the pipe.
func ( *Cmd) () (io.WriteCloser, error) {
if .Stdin != nil {
return nil, errors.New("exec: Stdin already set")
}
if .Process != nil {
return nil, errors.New("exec: StdinPipe after process started")
}
, , := os.Pipe()
if != nil {
return nil,
}
.Stdin =
.closeAfterStart = append(.closeAfterStart, )
:= &closeOnce{File: }
.closeAfterWait = append(.closeAfterWait, )
return , nil
}
type closeOnce struct {
*os.File
once sync.Once
err error
}
func ( *closeOnce) () error {
.once.Do(.close)
return .err
}
func ( *closeOnce) () {
.err = .File.Close()
}
// StdoutPipe returns a pipe that will be connected to the command's
// standard output when the command starts.
//
// Wait will close the pipe after seeing the command exit, so most callers
// need not close the pipe themselves. It is thus incorrect to call Wait
// before all reads from the pipe have completed.
// For the same reason, it is incorrect to call Run when using StdoutPipe.
// See the example for idiomatic usage.
func ( *Cmd) () (io.ReadCloser, error) {
if .Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
if .Process != nil {
return nil, errors.New("exec: StdoutPipe after process started")
}
, , := os.Pipe()
if != nil {
return nil,
}
.Stdout =
.closeAfterStart = append(.closeAfterStart, )
.closeAfterWait = append(.closeAfterWait, )
return , nil
}
// StderrPipe returns a pipe that will be connected to the command's
// standard error when the command starts.
//
// Wait will close the pipe after seeing the command exit, so most callers
// need not close the pipe themselves. It is thus incorrect to call Wait
// before all reads from the pipe have completed.
// For the same reason, it is incorrect to use Run when using StderrPipe.
// See the StdoutPipe example for idiomatic usage.
func ( *Cmd) () (io.ReadCloser, error) {
if .Stderr != nil {
return nil, errors.New("exec: Stderr already set")
}
if .Process != nil {
return nil, errors.New("exec: StderrPipe after process started")
}
, , := os.Pipe()
if != nil {
return nil,
}
.Stderr =
.closeAfterStart = append(.closeAfterStart, )
.closeAfterWait = append(.closeAfterWait, )
return , nil
}
// prefixSuffixSaver is an io.Writer which retains the first N bytes
// and the last N bytes written to it. The Bytes() methods reconstructs
// it with a pretty error message.
type prefixSuffixSaver struct {
N int // max size of prefix or suffix
prefix []byte
suffix []byte // ring buffer once len(suffix) == N
suffixOff int // offset to write into suffix
skipped int64
// TODO(bradfitz): we could keep one large []byte and use part of it for
// the prefix, reserve space for the '... Omitting N bytes ...' message,
// then the ring buffer suffix, and just rearrange the ring buffer
// suffix when Bytes() is called, but it doesn't seem worth it for
// now just for error messages. It's only ~64KB anyway.
}
func ( *prefixSuffixSaver) ( []byte) ( int, error) {
:= len()
= .fill(&.prefix, )
// Only keep the last w.N bytes of suffix data.
if := len() - .N; > 0 {
= [:]
.skipped += int64()
}
= .fill(&.suffix, )
// w.suffix is full now if p is non-empty. Overwrite it in a circle.
for len() > 0 { // 0, 1, or 2 iterations.
:= copy(.suffix[.suffixOff:], )
= [:]
.skipped += int64()
.suffixOff +=
if .suffixOff == .N {
.suffixOff = 0
}
}
return , nil
}
// fill appends up to len(p) bytes of p to *dst, such that *dst does not
// grow larger than w.N. It returns the un-appended suffix of p.
func ( *prefixSuffixSaver) ( *[]byte, []byte) ( []byte) {
if := .N - len(*); > 0 {
:= minInt(len(), )
* = append(*, [:]...)
= [:]
}
return
}
func ( *prefixSuffixSaver) () []byte {
if .suffix == nil {
return .prefix
}
if .skipped == 0 {
return append(.prefix, .suffix...)
}
var bytes.Buffer
.Grow(len(.prefix) + len(.suffix) + 50)
.Write(.prefix)
.WriteString("\n... omitting ")
.WriteString(strconv.FormatInt(.skipped, 10))
.WriteString(" bytes ...\n")
.Write(.suffix[.suffixOff:])
.Write(.suffix[:.suffixOff])
return .Bytes()
}
func (, int) int {
if < {
return
}
return
}
// dedupEnv returns a copy of env with any duplicates removed, in favor of
// later values.
// Items not of the normal environment "key=value" form are preserved unchanged.
func ( []string) []string {
return dedupEnvCase(runtime.GOOS == "windows", )
}
// dedupEnvCase is dedupEnv with a case option for testing.
// If caseInsensitive is true, the case of keys is ignored.
func ( bool, []string) []string {
:= make([]string, 0, len())
:= make(map[string]int, len()) // key => index into out
for , := range {
, , := strings.Cut(, "=")
if ! {
= append(, )
continue
}
if {
= strings.ToLower()
}
if , := []; {
[] =
continue
}
[] = len()
= append(, )
}
return
}
// addCriticalEnv adds any critical environment variables that are required
// (or at least almost always required) on the operating system.
// Currently this is only used for Windows.
func ( []string) []string {
if runtime.GOOS != "windows" {
return
}
for , := range {
, , := strings.Cut(, "=")
if ! {
continue
}
if strings.EqualFold(, "SYSTEMROOT") {
// We already have it.
return
}
}
return append(, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)