Source File
exec.go
Belonging Package
os/exec
package exec
import (
)
ExtraFiles []*os.File
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{}
}
:= new(strings.Builder)
.WriteString(.Path)
for , := range .Args[1:] {
.WriteByte(' ')
.WriteString()
}
return .String()
}
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()
}
}
, := LookPath()
if != nil {
return "",
}
:= strings.TrimPrefix(, )
return + , nil
}
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)
type ExitError struct {
*os.ProcessState
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
}
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(),
}
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()
}
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
}
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
}
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
}
func ( []string) []string {
return dedupEnvCase(runtime.GOOS == "windows", )
}
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin 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. |