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.//// # Executables in the current directory//// The functions Command and LookPath look for a program// in the directories listed in the current path, following the// conventions of the host operating system.// Operating systems have for decades included the current// directory in this search, sometimes implicitly and sometimes// configured explicitly that way by default.// Modern practice is that including the current directory// is usually unexpected and often leads to security problems.//// To avoid those security problems, as of Go 1.19, this package will not resolve a program// using an implicit or explicit path entry relative to the current directory.// That is, if you run exec.LookPath("go"), it will not successfully return// ./go on Unix nor .\go.exe on Windows, no matter how the path is configured.// Instead, if the usual path algorithms would result in that answer,// these functions return an error err satisfying errors.Is(err, ErrDot).//// For example, consider these two program snippets://// path, err := exec.LookPath("prog")// if err != nil {// log.Fatal(err)// }// use(path)//// and//// cmd := exec.Command("prog")// if err := cmd.Run(); err != nil {// log.Fatal(err)// }//// These will not find and run ./prog or .\prog.exe,// no matter how the current path is configured.//// Code that always wants to run a program from the current directory// can be rewritten to say "./prog" instead of "prog".//// Code that insists on including results from relative path entries// can instead override the error using an errors.Is check://// path, err := exec.LookPath("prog")// if errors.Is(err, exec.ErrDot) {// err = nil// }// if err != nil {// log.Fatal(err)// }// use(path)//// and//// cmd := exec.Command("prog")// if errors.Is(cmd.Err, exec.ErrDot) {// cmd.Err = nil// }// if err := cmd.Run(); err != nil {// log.Fatal(err)// }//// Setting the environment variable GODEBUG=execerrdot=0// disables generation of ErrDot entirely, temporarily restoring the pre-Go 1.19// behavior for programs that are unable to apply more targeted fixes.// A future version of Go may remove support for this variable.//// Before adding such overrides, make sure you understand the// security implications of doing so.// See https://go.dev/blog/path-security for more information.package execimport ()// 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 }// ErrWaitDelay is returned by (*Cmd).Wait if the process exits with a// successful status code but its output pipes are not closed before the// command's WaitDelay expires.var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete")// wrappedError wraps an error without relying on fmt.Errorf.type wrappedError struct {prefix stringerr error}func ( wrappedError) () string {return .prefix + ": " + .err.Error()}func ( wrappedError) () 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,// or because a nonzero WaitDelay was set and expired.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 or a nonzero WaitDelay// expires.//// 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.WriterStderr 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.// If the process was started successfully, Wait or Run will// populate its ProcessState when the command completes.ProcessState *os.ProcessState// ctx is the context passed to CommandContext, if any.ctx context.ContextErr error // LookPath error, if any.// If Cancel is non-nil, the command must have been created with// CommandContext and Cancel will be called when the command's// Context is done. By default, CommandContext sets Cancel to// call the Kill method on the command's Process.//// Typically a custom Cancel will send a signal to the command's// Process, but it may instead take other actions to initiate cancellation,// such as closing a stdin or stdout pipe or sending a shutdown request on a// network socket.//// If the command exits with a success status after Cancel is// called, and Cancel does not return an error equivalent to// os.ErrProcessDone, then Wait and similar methods will return a non-nil// error: either an error wrapping the one returned by Cancel,// or the error from the Context.// (If the command exits with a non-success status, or Cancel// returns an error that wraps os.ErrProcessDone, Wait and similar methods// continue to return the command's usual exit status.)//// If Cancel is set to nil, nothing will happen immediately when the command's// Context is done, but a nonzero WaitDelay will still take effect. That may// be useful, for example, to work around deadlocks in commands that do not// support shutdown signals but are expected to always finish quickly.//// Cancel will not be called if Start returns a non-nil error.Cancel func() error// If WaitDelay is non-zero, it bounds the time spent waiting on two sources// of unexpected delay in Wait: a child process that fails to exit after the// associated Context is canceled, and a child process that exits but leaves// its I/O pipes unclosed.//// The WaitDelay timer starts when either the associated Context is done or a// call to Wait observes that the child process has exited, whichever occurs// first. When the delay has elapsed, the command shuts down the child process// and/or its I/O pipes.//// If the child process has failed to exit — perhaps because it ignored or// failed to receive a shutdown signal from a Cancel function, or because no// Cancel function was set — then it will be terminated using os.Process.Kill.//// Then, if the I/O pipes communicating with the child process are still open,// those pipes are closed in order to unblock any goroutines currently blocked// on Read or Write calls.//// If pipes are closed due to WaitDelay, no Cancel call has occurred,// and the command has otherwise exited with a successful status, Wait and// similar methods will return ErrWaitDelay instead of nil.//// If WaitDelay is zero (the default), I/O pipes will be read until EOF,// which might not occur until orphaned subprocesses of the command have// also closed their descriptors for the pipes.WaitDelay time.Duration// childIOFiles holds closers for any of the child process's// stdin, stdout, and/or stderr files that were opened by the Cmd itself// (not supplied by the caller). These should be closed as soon as they// are inherited by the child process.childIOFiles []io.Closer// parentIOPipes holds closers for the parent's end of any pipes// connected to the child's stdin, stdout, and/or stderr streams// that were opened by the Cmd itself (not supplied by the caller).// These should be closed after Wait sees the command and copying// goroutines exit, or after WaitDelay has expired.parentIOPipes []io.Closer// goroutine holds a set of closures to execute to copy data// to and/or from the command's I/O pipes.goroutine []func() error// If goroutineErr is non-nil, it receives the first error from a copying// goroutine once all such goroutines have completed.// goroutineErr is set to nil once its error has been received.goroutineErr <-chan error// If ctxResult is non-nil, it receives the result of watchCtx exactly once.ctxResult <-chan ctxResult// The stack saved when the Command was created, if GODEBUG contains// execwait=2. Used for debugging leaks.createdByStack []byte// For a security release long ago, we created x/sys/execabs,// which manipulated the unexported lookPathErr error field// in this struct. For Go 1.19 we exported the field as Err error,// above, but we have to keep lookPathErr around for use by// old programs building against new toolchains.// The String and Start methods look for an error in lookPathErr// in preference to Err, to preserve the errors that execabs sets.//// In general we don't guarantee misuse of reflect like this,// but the misuse of reflect was by us, the best of various bad// options to fix the security problem, and people depend on// those old copies of execabs continuing to work.// The result is that we have to leave this variable around for the// rest of time, a compatibility scar.//// See https://go.dev/blog/path-security// and https://go.dev/issue/43724 for more context.lookPathErr error}// A ctxResult reports the result of watching the Context associated with a// running command (and sending corresponding signals if needed).type ctxResult struct {err error// If timer is non-nil, it expires after WaitDelay has elapsed after// the Context is done.//// (If timer is nil, that means that the Context was not done before the// command completed, or no WaitDelay was set, or the WaitDelay already// expired and its effect was already applied.)timer *time.Timer}var execwait = godebug.New("#execwait")var execerrdot = godebug.New("execerrdot")// 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 := execwait.Value(); != "" {if == "2" {// Obtain the caller stack. (This is equivalent to runtime/debug.Stack,// copied to avoid importing the whole package.):= make([]byte, 1024)for {:= runtime.Stack(, false)if < len() {= [:]break}= make([]byte, 2*len())}if := bytes.Index(, []byte("\nos/exec.Command(")); >= 0 {= [+1:]}.createdByStack =}runtime.SetFinalizer(, func( *Cmd) {if .Process != nil && .ProcessState == nil {:= ""if .createdByStack == nil {= " (set GODEBUG=execwait=2 to capture stacks for debugging)"} else {os.Stderr.WriteString("GODEBUG=execwait=2 detected a leaked exec.Cmd created by:\n")os.Stderr.Write(.createdByStack)os.Stderr.WriteString("\n")= ""}panic("exec: Cmd started a Process but leaked without a call to Wait" + )}})}if filepath.Base() == {, := LookPath()if != "" {// Update cmd.Path even if err is non-nil.// If err is ErrDot (especially on Windows), lp may include a resolved// extension (like .exe or .bat) that should be preserved..Path =}if != nil {.Err =}}return}// CommandContext is like Command but includes a context.//// The provided context is used to interrupt the process// (by calling cmd.Cancel or os.Process.Kill)// if the context becomes done before the command completes on its own.//// CommandContext sets the command's Cancel function to invoke the Kill method// on its Process, and leaves its WaitDelay unset. The caller may change the// cancellation behavior by modifying those fields before starting the command.func ( context.Context, string, ...string) *Cmd {if == nil {panic("nil Context")}:= Command(, ...).ctx =.Cancel = func() error {return .Process.Kill()}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 .Err != nil || .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 interfaceEqual(, any) bool {defer func() {recover()}()return ==}func ( *Cmd) () []string {if len(.Args) > 0 {return .Args}return []string{.Path}}func ( *Cmd) () (*os.File, error) {if .Stdin == nil {, := os.Open(os.DevNull)if != nil {return nil,}.childIOFiles = append(.childIOFiles, )return , nil}if , := .Stdin.(*os.File); {return , nil}, , := os.Pipe()if != nil {return nil,}.childIOFiles = append(.childIOFiles, ).parentIOPipes = append(.parentIOPipes, ).goroutine = append(.goroutine, func() error {, := io.Copy(, .Stdin)if skipStdinCopyError() {= nil}if := .Close(); == nil {=}return})return , nil}func ( *Cmd) () (*os.File, error) {return .writerDescriptor(.Stdout)}func ( *Cmd) ( *os.File) (*os.File, error) {if .Stderr != nil && interfaceEqual(.Stderr, .Stdout) {return , nil}return .writerDescriptor(.Stderr)}// writerDescriptor returns an os.File to which the child process// can write to send data to w.//// If w is nil, writerDescriptor returns a File that writes to os.DevNull.func ( *Cmd) ( io.Writer) (*os.File, error) {if == nil {, := os.OpenFile(os.DevNull, os.O_WRONLY, 0)if != nil {return nil,}.childIOFiles = append(.childIOFiles, )return , nil}if , := .(*os.File); {return , nil}, , := os.Pipe()if != nil {return nil,}.childIOFiles = append(.childIOFiles, ).parentIOPipes = append(.parentIOPipes, ).goroutine = append(.goroutine, func() error {, := io.Copy(, ).Close() // in case io.Copy stopped due to write errorreturn})return , nil}func closeDescriptors( []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 lookExtensions(, string) (string, error) {if filepath.Base() == {= "." + string(filepath.Separator) +}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.//// After a successful call to Start the Wait method must be called in// order to release associated system resources.func ( *Cmd) () error {// Check for doubled Start calls before we defer failure cleanup. If the prior// call to Start succeeded, we don't want to spuriously close its pipes.if .Process != nil {return errors.New("exec: already started")}:= falsedefer func() {closeDescriptors(.childIOFiles).childIOFiles = nilif ! {closeDescriptors(.parentIOPipes).parentIOPipes = nil}}()if .Path == "" && .Err == nil && .lookPathErr == nil {.Err = errors.New("exec: no command")}if .Err != nil || .lookPathErr != nil {if .lookPathErr != nil {return .lookPathErr}return .Err}if runtime.GOOS == "windows" {, := lookExtensions(.Path, .Dir)if != nil {return}.Path =}if .Cancel != nil && .ctx == nil {return errors.New("exec: command with a non-nil Cancel was not created with CommandContext")}if .ctx != nil {select {case <-.ctx.Done():return .ctx.Err()default:}}:= make([]*os.File, 0, 3+len(.ExtraFiles)), := .childStdin()if != nil {return}= append(, ), := .childStdout()if != nil {return}= append(, ), := .childStderr()if != nil {return}= append(, )= append(, .ExtraFiles...), := .environ()if != nil {return}.Process, = os.StartProcess(.Path, .argv(), &os.ProcAttr{Dir: .Dir,Files: ,Env: ,Sys: .SysProcAttr,})if != nil {return}= true// Don't allocate the goroutineErr channel unless there are goroutines to start.if len(.goroutine) > 0 {:= make(chan error, 1).goroutineErr =type struct {interror}:= make(chan , 1)<- {: len(.goroutine)}for , := range .goroutine {go func( func() error) {:= ():= <-if . == nil {. =}.--if . == 0 {<- .} else {<-}}()}.goroutine = nil // Allow the goroutines' closures to be GC'd when they complete.}// If we have anything to do when the command's Context expires,// start a goroutine to watch for cancellation.//// (Even if the command was created by CommandContext, a helper library may// have explicitly set its Cancel field back to nil, indicating that it should// be allowed to continue running after cancellation after all.)if (.Cancel != nil || .WaitDelay != 0) && .ctx != nil && .ctx.Done() != nil {:= make(chan ctxResult).ctxResult =go .watchCtx()}return nil}// watchCtx watches c.ctx until it is able to send a result to resultc.//// If c.ctx is done before a result can be sent, watchCtx calls c.Cancel,// and/or kills cmd.Process it after c.WaitDelay has elapsed.//// watchCtx manipulates c.goroutineErr, so its result must be received before// c.awaitGoroutines is called.func ( *Cmd) ( chan<- ctxResult) {select {case <- ctxResult{}:returncase <-.ctx.Done():}var errorif .Cancel != nil {if := .Cancel(); == nil {// We appear to have successfully interrupted the command, so any// program behavior from this point may be due to ctx even if the// command exits with code 0.= .ctx.Err()} else if errors.Is(, os.ErrProcessDone) {// The process already finished: we just didn't notice it yet.// (Perhaps c.Wait hadn't been called, or perhaps it happened to race with// c.ctx being cancelled.) Don't inject a needless error.} else {= wrappedError{prefix: "exec: canceling Cmd",err: ,}}}if .WaitDelay == 0 {<- ctxResult{err: }return}:= time.NewTimer(.WaitDelay)select {case <- ctxResult{err: , timer: }:// c.Process.Wait returned and we've handed the timer off to c.Wait.// It will take care of goroutine shutdown from here.returncase <-.C:}:= falseif := .Process.Kill(); == nil {// We appear to have killed the process. c.Process.Wait should return a// non-nil error to c.Wait unless the Kill signal races with a successful// exit, and if that does happen we shouldn't report a spurious error,// so don't set err to anything here.= true} else if !errors.Is(, os.ErrProcessDone) {= wrappedError{prefix: "exec: killing Cmd",err: ,}}if .goroutineErr != nil {select {case := <-.goroutineErr:// Forward goroutineErr only if we don't have reason to believe it was// caused by a call to Cancel or Kill above.if == nil && ! {=}default:// Close the child process's I/O pipes, in case it abandoned some// subprocess that inherited them and is still holding them open// (see https://go.dev/issue/23019).//// We close the goroutine pipes only after we have sent any signals we're// going to send to the process (via Signal or Kill above): if we send// SIGKILL to the process, we would prefer for it to die of SIGKILL, not// SIGPIPE. (However, this may still cause any orphaned subprocesses to// terminate with SIGPIPE.)closeDescriptors(.parentIOPipes)// Wait for the copying goroutines to finish, but report ErrWaitDelay for// the error: any other error here could result from closing the pipes._ = <-.goroutineErrif == nil {= ErrWaitDelay}}// Since we have already received the only result from c.goroutineErr,// set it to nil to prevent awaitGoroutines from blocking on it..goroutineErr = nil}<- ctxResult{err: }}// 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 .ProcessState != nil {return errors.New("exec: Wait was already called")}, := .Process.Wait()if == nil && !.Success() {= &ExitError{ProcessState: }}.ProcessState =var *time.Timerif .ctxResult != nil {:= <-.ctxResult= .timer// If c.Process.Wait returned an error, prefer that.// Otherwise, report any error from the watchCtx goroutine,// such as a Context cancellation or a WaitDelay overrun.if == nil && .err != nil {= .err}}if := .awaitGoroutines(); == nil {// Report an error from the copying goroutines only if the program otherwise// exited normally on its own. Otherwise, the copying error may be due to the// abnormal termination.=}closeDescriptors(.parentIOPipes).parentIOPipes = nilreturn}// awaitGoroutines waits for the results of the goroutines copying data to or// from the command's I/O pipes.//// If c.WaitDelay elapses before the goroutines complete, awaitGoroutines// forcibly closes their pipes and returns ErrWaitDelay.//// If timer is non-nil, it must send to timer.C at the end of c.WaitDelay.func ( *Cmd) ( *time.Timer) error {defer func() {if != nil {.Stop()}.goroutineErr = nil}()if .goroutineErr == nil {return nil // No running goroutines to await.}if == nil {if .WaitDelay == 0 {return <-.goroutineErr}select {case := <-.goroutineErr:// Avoid the overhead of starting a timer.returndefault:}// No existing timer was started: either there is no Context associated with// the command, or c.Process.Wait completed before the Context was done.= time.NewTimer(.WaitDelay)}select {case <-.C:closeDescriptors(.parentIOPipes)// Wait for the copying goroutines to finish, but ignore any error// (since it was probably caused by closing the pipes)._ = <-.goroutineErrreturn ErrWaitDelaycase := <-.goroutineErr: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 == 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 {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 =.childIOFiles = append(.childIOFiles, ).parentIOPipes = append(.parentIOPipes, )return , nil}// 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 =.childIOFiles = append(.childIOFiles, ).parentIOPipes = append(.parentIOPipes, )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 =.childIOFiles = append(.childIOFiles, ).parentIOPipes = append(.parentIOPipes, )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 suffixprefix []bytesuffix []byte // ring buffer once len(suffix) == NsuffixOff int // offset to write into suffixskipped 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 minInt(, int) int {if < {return}return}// environ returns a best-effort copy of the environment in which the command// would be run as it is currently configured. If an error occurs in computing// the environment, it is returned alongside the best-effort copy.func ( *Cmd) () ([]string, error) {var error:= .Envif == nil {, = execenv.Default(.SysProcAttr)if != nil {= os.Environ()// Note that the non-nil err is preserved despite env being overridden.}if .Dir != "" {switch runtime.GOOS {case "windows", "plan9":// Windows and Plan 9 do not use the PWD variable, so we don't need to// keep it accurate.default:// On POSIX platforms, PWD represents “an absolute pathname of the// current working directory.” Since we are changing the working// directory for the command, we should also update PWD to reflect that.//// Unfortunately, we didn't always do that, so (as proposed in// https://go.dev/issue/50599) to avoid unintended collateral damage we// only implicitly update PWD when Env is nil. That way, we're much// less likely to override an intentional change to the variable.if , := filepath.Abs(.Dir); == nil {= append(, "PWD="+)} else if == nil {=}}}}, := dedupEnv()if == nil {=}return addCriticalEnv(),}// Environ returns a copy of the environment in which the command would be run// as it is currently configured.func ( *Cmd) () []string {// Intentionally ignore errors: environ returns a best-effort environment no matter what., := .environ()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.// Except on Plan 9, items containing NUL characters are removed, and// an error is returned along with the remaining values.func dedupEnv( []string) ([]string, error) {return dedupEnvCase(runtime.GOOS == "windows", runtime.GOOS == "plan9", )}// dedupEnvCase is dedupEnv with a case option for testing.// If caseInsensitive is true, the case of keys is ignored.// If nulOK is false, items containing NUL characters are allowed.func dedupEnvCase(, bool, []string) ([]string, error) {// Construct the output in reverse order, to preserve the// last occurrence of each key.var error:= make([]string, 0, len()):= make(map[string]bool, len())for := len(); > 0; -- {:= [-1]// Reject NUL in environment variables to prevent security issues (#56284);// except on Plan 9, which uses NUL as os.PathListSeparator (#56544).if ! && strings.IndexByte(, 0) != -1 {= errors.New("exec: environment variable contains NUL")continue}:= strings.Index(, "=")if == 0 {// We observe in practice keys with a single leading "=" on Windows.// TODO(#49886): Should we consume only the first leading "=" as part// of the key, or parse through arbitrarily many of them until a non-"="?= strings.Index([1:], "=") + 1}if < 0 {if != "" {// The entry is not of the form "key=value" (as it is required to be).// Leave it as-is for now.// TODO(#52436): should we strip or reject these bogus entries?= append(, )}continue}:= [:]if {= strings.ToLower()}if [] {continue}[] = true= append(, )}// Now reverse the slice to restore the original order.for := 0; < len()/2; ++ {:= len() - - 1[], [] = [], []}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 addCriticalEnv( []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"))}// ErrDot indicates that a path lookup resolved to an executable// in the current directory due to ‘.’ being in the path, either// implicitly or explicitly. See the package documentation for details.//// Note that functions in this package do not return ErrDot directly.// Code should use errors.Is(err, ErrDot), not err == ErrDot,// to test whether a returned error err is due to this condition.var ErrDot = errors.New("cannot run executable found relative to current directory")
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |