618 lines · 16.6 KB · Go
RawDownload
// 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.package exec import (	"bytes"	"errors"	"io"	"os"	"path/filepath"	"runtime"	"strconv"	"strings"	"sync"	"syscall") // Error records the name of a binary that failed to be executed// and the reason it failed.type Error struct {	Name string	Err  error} func (e *Error) Error() string {	return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error()} // 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.	// If Env is nil, Run uses the current process's environment.	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 Stdout and Stderr are the same writer, 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.	//	// BUG(rsc): On OS X 10.6, child processes may sometimes inherit unwanted fds.	// https://golang.org/issue/2603	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 	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} // 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 the path to a complete name if possible. Otherwise it uses// name directly.//// 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")func Command(name string, arg ...string) *Cmd {	cmd := &Cmd{		Path: name,		Args: append([]string{name}, arg...),	}	if filepath.Base(name) == name {		if lp, err := LookPath(name); err != nil {			cmd.lookPathErr = err		} else {			cmd.Path = lp		}	}	return cmd} // interfaceEqual protects against panics from doing equality tests on// two interfaces with non-comparable underlying types.func interfaceEqual(a, b interface{}) bool {	defer func() {		recover()	}()	return a == b} func (c *Cmd) envv() []string {	if c.Env != nil {		return c.Env	}	return os.Environ()} func (c *Cmd) argv() []string {	if len(c.Args) > 0 {		return c.Args	}	return []string{c.Path}} // skipStdinCopyError optionally specifies a function which reports// whether the provided the stdin copy error should be ignored.// It is non-nil everywhere but Plan 9, which lacks EPIPE. See exec_posix.go.var skipStdinCopyError func(error) bool func (c *Cmd) stdin() (f *os.File, err error) {	if c.Stdin == nil {		f, err = os.Open(os.DevNull)		if err != nil {			return		}		c.closeAfterStart = append(c.closeAfterStart, f)		return	} 	if f, ok := c.Stdin.(*os.File); ok {		return f, nil	} 	pr, pw, err := os.Pipe()	if err != nil {		return	} 	c.closeAfterStart = append(c.closeAfterStart, pr)	c.closeAfterWait = append(c.closeAfterWait, pw)	c.goroutine = append(c.goroutine, func() error {		_, err := io.Copy(pw, c.Stdin)		if skip := skipStdinCopyError; skip != nil && skip(err) {			err = nil		}		if err1 := pw.Close(); err == nil {			err = err1		}		return err	})	return pr, nil} func (c *Cmd) stdout() (f *os.File, err error) {	return c.writerDescriptor(c.Stdout)} func (c *Cmd) stderr() (f *os.File, err error) {	if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) {		return c.childFiles[1], nil	}	return c.writerDescriptor(c.Stderr)} func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {	if w == nil {		f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)		if err != nil {			return		}		c.closeAfterStart = append(c.closeAfterStart, f)		return	} 	if f, ok := w.(*os.File); ok {		return f, nil	} 	pr, pw, err := os.Pipe()	if err != nil {		return	} 	c.closeAfterStart = append(c.closeAfterStart, pw)	c.closeAfterWait = append(c.closeAfterWait, pr)	c.goroutine = append(c.goroutine, func() error {		_, err := io.Copy(w, pr)		pr.Close() // in case io.Copy stopped due to write error		return err	})	return pw, nil} func (c *Cmd) closeDescriptors(closers []io.Closer) {	for _, fd := range closers {		fd.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 fails to run or doesn't complete successfully, the// error is of type *ExitError. Other error types may be// returned for I/O problems.func (c *Cmd) Run() error {	if err := c.Start(); err != nil {		return err	}	return c.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(path, dir string) (string, error) {	if filepath.Base(path) == path {		path = filepath.Join(".", path)	}	if dir == "" {		return LookPath(path)	}	if filepath.VolumeName(path) != "" {		return LookPath(path)	}	if len(path) > 1 && os.IsPathSeparator(path[0]) {		return LookPath(path)	}	dirandpath := filepath.Join(dir, path)	// We assume that LookPath will only add file extension.	lp, err := LookPath(dirandpath)	if err != nil {		return "", err	}	ext := strings.TrimPrefix(lp, dirandpath)	return path + ext, nil} // Start starts the specified command but does not wait for it to complete.//// The Wait method will return the exit code and release associated resources// once the command exits.func (c *Cmd) Start() error {	if c.lookPathErr != nil {		c.closeDescriptors(c.closeAfterStart)		c.closeDescriptors(c.closeAfterWait)		return c.lookPathErr	}	if runtime.GOOS == "windows" {		lp, err := lookExtensions(c.Path, c.Dir)		if err != nil {			c.closeDescriptors(c.closeAfterStart)			c.closeDescriptors(c.closeAfterWait)			return err		}		c.Path = lp	}	if c.Process != nil {		return errors.New("exec: already started")	} 	type F func(*Cmd) (*os.File, error)	for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {		fd, err := setupFd(c)		if err != nil {			c.closeDescriptors(c.closeAfterStart)			c.closeDescriptors(c.closeAfterWait)			return err		}		c.childFiles = append(c.childFiles, fd)	}	c.childFiles = append(c.childFiles, c.ExtraFiles...) 	var err error	c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{		Dir:   c.Dir,		Files: c.childFiles,		Env:   c.envv(),		Sys:   c.SysProcAttr,	})	if err != nil {		c.closeDescriptors(c.closeAfterStart)		c.closeDescriptors(c.closeAfterWait)		return err	} 	c.closeDescriptors(c.closeAfterStart) 	c.errch = make(chan error, len(c.goroutine))	for _, fn := range c.goroutine {		go func(fn func() error) {			c.errch <- fn()		}(fn)	} 	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 (e *ExitError) Error() string {	return e.ProcessState.String()} // Wait waits for the command to exit.// It 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 c.Stdin is not an *os.File, Wait also waits for the I/O loop// copying from c.Stdin into the process's standard input// to complete.//// Wait releases any resources associated with the Cmd.func (c *Cmd) Wait() error {	if c.Process == nil {		return errors.New("exec: not started")	}	if c.finished {		return errors.New("exec: Wait was already called")	}	c.finished = true	state, err := c.Process.Wait()	c.ProcessState = state 	var copyError error	for range c.goroutine {		if err := <-c.errch; err != nil && copyError == nil {			copyError = err		}	} 	c.closeDescriptors(c.closeAfterWait) 	if err != nil {		return err	} else if !state.Success() {		return &ExitError{ProcessState: state}	} 	return copyError} // 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 (c *Cmd) Output() ([]byte, error) {	if c.Stdout != nil {		return nil, errors.New("exec: Stdout already set")	}	var stdout bytes.Buffer	c.Stdout = &stdout 	captureErr := c.Stderr == nil	if captureErr {		c.Stderr = &prefixSuffixSaver{N: 32 << 10}	} 	err := c.Run()	if err != nil && captureErr {		if ee, ok := err.(*ExitError); ok {			ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes()		}	}	return stdout.Bytes(), err} // CombinedOutput runs the command and returns its combined standard// output and standard error.func (c *Cmd) CombinedOutput() ([]byte, error) {	if c.Stdout != nil {		return nil, errors.New("exec: Stdout already set")	}	if c.Stderr != nil {		return nil, errors.New("exec: Stderr already set")	}	var b bytes.Buffer	c.Stdout = &b	c.Stderr = &b	err := c.Run()	return b.Bytes(), err} // 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 (c *Cmd) StdinPipe() (io.WriteCloser, error) {	if c.Stdin != nil {		return nil, errors.New("exec: Stdin already set")	}	if c.Process != nil {		return nil, errors.New("exec: StdinPipe after process started")	}	pr, pw, err := os.Pipe()	if err != nil {		return nil, err	}	c.Stdin = pr	c.closeAfterStart = append(c.closeAfterStart, pr)	wc := &closeOnce{File: pw}	c.closeAfterWait = append(c.closeAfterWait, wc)	return wc, nil} type closeOnce struct {	*os.File 	once sync.Once	err  error} func (c *closeOnce) Close() error {	c.once.Do(c.close)	return c.err} func (c *closeOnce) close() {	c.err = c.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; however, an implication is that// it is 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.

Lines 1–500 of 618.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t