// 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")) }