// 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 execimport ()// Error is returned by LookPath when it fails to classify a file as an// executable.typeErrorstruct {// Name is the file name for which the error occurred.Namestring// Err is the underlying error.Errerror}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.typeCmdstruct {// 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.Pathstring// 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.Dirstring// 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.Stdinio.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.Stdoutio.WriterStderrio.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.ProcessStatectxcontext.Context// nil means nonelookPathErrerror// LookPath error, if any.finishedbool// when Wait was calledchildFiles []*os.FilecloseAfterStart []io.ClosercloseAfterWait []io.Closergoroutine []func() errorerrchchanerror// one send per goroutinewaitDonechanstruct{}}// 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{}, ...), }iffilepath.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)returnstrings.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 (, interface{}) bool {deferfunc() {recover() }()return == }func ( *Cmd) () ([]string, error) {if .Env != nil {return .Env, nil }returnexecenv.Default(.SysProcAttr)}func ( *Cmd) () []string {iflen(.Args) > 0 {return .Args }return []string{.Path}}// skipStdinCopyError optionally specifies a function which reports// whether the provided stdin copy error should be ignored.varskipStdinCopyErrorfunc(error) boolfunc ( *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 errorreturn })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) {iffilepath.Base() == { = filepath.Join(".", ) }if == "" {returnLookPath() }iffilepath.VolumeName() != "" {returnLookPath() }iflen() > 1 && os.IsPathSeparator([0]) {returnLookPath() } := 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 }ifruntime.GOOS == "windows" { , := lookExtensions(.Path, .Dir)if != nil { .closeDescriptors(.closeAfterStart) .closeDescriptors(.closeAfterWait)return } .Path = }if .Process != nil {returnerrors.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))typefunc(*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.iflen(.goroutine) > 0 { .errch = make(chanerror, len(.goroutine))for , := range .goroutine {gofunc( func() error) { .errch <- () }() } }if .ctx != nil { .waitDone = make(chanstruct{})gofunc() {select {case<-.ctx.Done(): .Process.Kill()case<-.waitDone: } }() }returnnil}// An ExitError reports an unsuccessful exit by a command.typeExitErrorstruct { *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 {returnerrors.New("exec: not started") }if .finished {returnerrors.New("exec: Wait was already called") } .finished = true , := .Process.Wait()if .waitDone != nil {close(.waitDone) } .ProcessState = varerrorforrange .goroutine {if := <-.errch; != nil && == nil { = } } .closeDescriptors(.closeAfterWait)if != nil {return } elseif !.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 {returnnil, errors.New("exec: Stdout already set") }varbytes.Buffer .Stdout = & := .Stderr == nilif { .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 {returnnil, errors.New("exec: Stdout already set") }if .Stderr != nil {returnnil, errors.New("exec: Stderr already set") }varbytes.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 {returnnil, errors.New("exec: Stdin already set") }if .Process != nil {returnnil, errors.New("exec: StdinPipe after process started") } , , := os.Pipe()if != nil {returnnil, } .Stdin = .closeAfterStart = append(.closeAfterStart, ) := &closeOnce{File: } .closeAfterWait = append(.closeAfterWait, )return , nil}typecloseOncestruct { *os.Fileoncesync.Onceerrerror}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 {returnnil, errors.New("exec: Stdout already set") }if .Process != nil {returnnil, errors.New("exec: StdoutPipe after process started") } , , := os.Pipe()if != nil {returnnil, } .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 {returnnil, errors.New("exec: Stderr already set") }if .Process != nil {returnnil, errors.New("exec: StderrPipe after process started") } , , := os.Pipe()if != nil {returnnil, } .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.typeprefixSuffixSaverstruct {Nint// max size of prefix or suffixprefix []bytesuffix []byte// ring buffer once len(suffix) == NsuffixOffint// offset to write into suffixskippedint64// 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.forlen() > 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 {returnappend(.prefix, .suffix...) }varbytes.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 {returndedupEnvCase(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 outfor , := range { := strings.Index(, "=")if < 0 { = 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 {ifruntime.GOOS != "windows" {return }for , := range { := strings.Index(, "=")if < 0 {continue } := [:]ifstrings.EqualFold(, "SYSTEMROOT") {// We already have it.return } }returnappend(, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))}
The pages are generated with Goldsv0.2.8-preview. (GOOS=darwin GOARCH=arm64)