github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/os/exec/exec.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package exec runs external commands. It wraps os.StartProcess to make it 6 // easier to remap stdin and stdout, connect I/O with pipes, and do other 7 // adjustments. 8 // 9 // Unlike the "system" library call from C and other languages, the 10 // os/exec package intentionally does not invoke the system shell and 11 // does not expand any glob patterns or handle other expansions, 12 // pipelines, or redirections typically done by shells. The package 13 // behaves more like C's "exec" family of functions. To expand glob 14 // patterns, either call the shell directly, taking care to escape any 15 // dangerous input, or use the path/filepath package's Glob function. 16 // To expand environment variables, use package os's ExpandEnv. 17 // 18 // Note that the examples in this package assume a Unix system. 19 // They may not run on Windows, and they do not run in the Go Playground 20 // used by golang.org and godoc.org. 21 package exec 22 23 import ( 24 "bytes" 25 "context" 26 "errors" 27 "internal/syscall/execenv" 28 "io" 29 "os" 30 "path/filepath" 31 "runtime" 32 "strconv" 33 "strings" 34 "sync" 35 "syscall" 36 ) 37 38 // Error is returned by LookPath when it fails to classify a file as an 39 // executable. 40 type Error struct { 41 // Name is the file name for which the error occurred. 42 Name string 43 // Err is the underlying error. 44 Err error 45 } 46 47 func (e *Error) Error() string { 48 return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error() 49 } 50 51 func (e *Error) Unwrap() error { return e.Err } 52 53 // Cmd represents an external command being prepared or run. 54 // 55 // A Cmd cannot be reused after calling its Run, Output or CombinedOutput 56 // methods. 57 type Cmd struct { 58 // Path is the path of the command to run. 59 // 60 // This is the only field that must be set to a non-zero 61 // value. If Path is relative, it is evaluated relative 62 // to Dir. 63 Path string 64 65 // Args holds command line arguments, including the command as Args[0]. 66 // If the Args field is empty or nil, Run uses {Path}. 67 // 68 // In typical use, both Path and Args are set by calling Command. 69 Args []string 70 71 // Env specifies the environment of the process. 72 // Each entry is of the form "key=value". 73 // If Env is nil, the new process uses the current process's 74 // environment. 75 // If Env contains duplicate environment keys, only the last 76 // value in the slice for each duplicate key is used. 77 // As a special case on Windows, SYSTEMROOT is always added if 78 // missing and not explicitly set to the empty string. 79 Env []string 80 81 // Dir specifies the working directory of the command. 82 // If Dir is the empty string, Run runs the command in the 83 // calling process's current directory. 84 Dir string 85 86 // Stdin specifies the process's standard input. 87 // 88 // If Stdin is nil, the process reads from the null device (os.DevNull). 89 // 90 // If Stdin is an *os.File, the process's standard input is connected 91 // directly to that file. 92 // 93 // Otherwise, during the execution of the command a separate 94 // goroutine reads from Stdin and delivers that data to the command 95 // over a pipe. In this case, Wait does not complete until the goroutine 96 // stops copying, either because it has reached the end of Stdin 97 // (EOF or a read error) or because writing to the pipe returned an error. 98 Stdin io.Reader 99 100 // Stdout and Stderr specify the process's standard output and error. 101 // 102 // If either is nil, Run connects the corresponding file descriptor 103 // to the null device (os.DevNull). 104 // 105 // If either is an *os.File, the corresponding output from the process 106 // is connected directly to that file. 107 // 108 // Otherwise, during the execution of the command a separate goroutine 109 // reads from the process over a pipe and delivers that data to the 110 // corresponding Writer. In this case, Wait does not complete until the 111 // goroutine reaches EOF or encounters an error. 112 // 113 // If Stdout and Stderr are the same writer, and have a type that can 114 // be compared with ==, at most one goroutine at a time will call Write. 115 Stdout io.Writer 116 Stderr io.Writer 117 118 // ExtraFiles specifies additional open files to be inherited by the 119 // new process. It does not include standard input, standard output, or 120 // standard error. If non-nil, entry i becomes file descriptor 3+i. 121 // 122 // ExtraFiles is not supported on Windows. 123 ExtraFiles []*os.File 124 125 // SysProcAttr holds optional, operating system-specific attributes. 126 // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. 127 SysProcAttr *syscall.SysProcAttr 128 129 // Process is the underlying process, once started. 130 Process *os.Process 131 132 // ProcessState contains information about an exited process, 133 // available after a call to Wait or Run. 134 ProcessState *os.ProcessState 135 136 ctx context.Context // nil means none 137 lookPathErr error // LookPath error, if any. 138 finished bool // when Wait was called 139 childFiles []*os.File 140 closeAfterStart []io.Closer 141 closeAfterWait []io.Closer 142 goroutine []func() error 143 errch chan error // one send per goroutine 144 waitDone chan struct{} 145 } 146 147 // Command returns the Cmd struct to execute the named program with 148 // the given arguments. 149 // 150 // It sets only the Path and Args in the returned structure. 151 // 152 // If name contains no path separators, Command uses LookPath to 153 // resolve name to a complete path if possible. Otherwise it uses name 154 // directly as Path. 155 // 156 // The returned Cmd's Args field is constructed from the command name 157 // followed by the elements of arg, so arg should not include the 158 // command name itself. For example, Command("echo", "hello"). 159 // Args[0] is always name, not the possibly resolved Path. 160 // 161 // On Windows, processes receive the whole command line as a single string 162 // and do their own parsing. Command combines and quotes Args into a command 163 // line string with an algorithm compatible with applications using 164 // CommandLineToArgvW (which is the most common way). Notable exceptions are 165 // msiexec.exe and cmd.exe (and thus, all batch files), which have a different 166 // unquoting algorithm. In these or other similar cases, you can do the 167 // quoting yourself and provide the full command line in SysProcAttr.CmdLine, 168 // leaving Args empty. 169 func Command(name string, arg ...string) *Cmd { 170 cmd := &Cmd{ 171 Path: name, 172 Args: append([]string{name}, arg...), 173 } 174 if filepath.Base(name) == name { 175 if lp, err := LookPath(name); err != nil { 176 cmd.lookPathErr = err 177 } else { 178 cmd.Path = lp 179 } 180 } 181 return cmd 182 } 183 184 // CommandContext is like Command but includes a context. 185 // 186 // The provided context is used to kill the process (by calling 187 // os.Process.Kill) if the context becomes done before the command 188 // completes on its own. 189 func CommandContext(ctx context.Context, name string, arg ...string) *Cmd { 190 if ctx == nil { 191 panic("nil Context") 192 } 193 cmd := Command(name, arg...) 194 cmd.ctx = ctx 195 return cmd 196 } 197 198 // String returns a human-readable description of c. 199 // It is intended only for debugging. 200 // In particular, it is not suitable for use as input to a shell. 201 // The output of String may vary across Go releases. 202 func (c *Cmd) String() string { 203 if c.lookPathErr != nil { 204 // failed to resolve path; report the original requested path (plus args) 205 return strings.Join(c.Args, " ") 206 } 207 // report the exact executable path (plus args) 208 b := new(strings.Builder) 209 b.WriteString(c.Path) 210 for _, a := range c.Args[1:] { 211 b.WriteByte(' ') 212 b.WriteString(a) 213 } 214 return b.String() 215 } 216 217 // interfaceEqual protects against panics from doing equality tests on 218 // two interfaces with non-comparable underlying types. 219 func interfaceEqual(a, b interface{}) bool { 220 defer func() { 221 recover() 222 }() 223 return a == b 224 } 225 226 func (c *Cmd) envv() ([]string, error) { 227 if c.Env != nil { 228 return c.Env, nil 229 } 230 return execenv.Default(c.SysProcAttr) 231 } 232 233 func (c *Cmd) argv() []string { 234 if len(c.Args) > 0 { 235 return c.Args 236 } 237 return []string{c.Path} 238 } 239 240 // skipStdinCopyError optionally specifies a function which reports 241 // whether the provided stdin copy error should be ignored. 242 var skipStdinCopyError func(error) bool 243 244 func (c *Cmd) stdin() (f *os.File, err error) { 245 if c.Stdin == nil { 246 f, err = os.Open(os.DevNull) 247 if err != nil { 248 return 249 } 250 c.closeAfterStart = append(c.closeAfterStart, f) 251 return 252 } 253 254 if f, ok := c.Stdin.(*os.File); ok { 255 return f, nil 256 } 257 258 pr, pw, err := os.Pipe() 259 if err != nil { 260 return 261 } 262 263 c.closeAfterStart = append(c.closeAfterStart, pr) 264 c.closeAfterWait = append(c.closeAfterWait, pw) 265 c.goroutine = append(c.goroutine, func() error { 266 _, err := io.Copy(pw, c.Stdin) 267 if skip := skipStdinCopyError; skip != nil && skip(err) { 268 err = nil 269 } 270 if err1 := pw.Close(); err == nil { 271 err = err1 272 } 273 return err 274 }) 275 return pr, nil 276 } 277 278 func (c *Cmd) stdout() (f *os.File, err error) { 279 return c.writerDescriptor(c.Stdout) 280 } 281 282 func (c *Cmd) stderr() (f *os.File, err error) { 283 if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { 284 return c.childFiles[1], nil 285 } 286 return c.writerDescriptor(c.Stderr) 287 } 288 289 func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { 290 if w == nil { 291 f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) 292 if err != nil { 293 return 294 } 295 c.closeAfterStart = append(c.closeAfterStart, f) 296 return 297 } 298 299 if f, ok := w.(*os.File); ok { 300 return f, nil 301 } 302 303 pr, pw, err := os.Pipe() 304 if err != nil { 305 return 306 } 307 308 c.closeAfterStart = append(c.closeAfterStart, pw) 309 c.closeAfterWait = append(c.closeAfterWait, pr) 310 c.goroutine = append(c.goroutine, func() error { 311 _, err := io.Copy(w, pr) 312 pr.Close() // in case io.Copy stopped due to write error 313 return err 314 }) 315 return pw, nil 316 } 317 318 func (c *Cmd) closeDescriptors(closers []io.Closer) { 319 for _, fd := range closers { 320 fd.Close() 321 } 322 } 323 324 // Run starts the specified command and waits for it to complete. 325 // 326 // The returned error is nil if the command runs, has no problems 327 // copying stdin, stdout, and stderr, and exits with a zero exit 328 // status. 329 // 330 // If the command starts but does not complete successfully, the error is of 331 // type *ExitError. Other error types may be returned for other situations. 332 // 333 // If the calling goroutine has locked the operating system thread 334 // with runtime.LockOSThread and modified any inheritable OS-level 335 // thread state (for example, Linux or Plan 9 name spaces), the new 336 // process will inherit the caller's thread state. 337 func (c *Cmd) Run() error { 338 if err := c.Start(); err != nil { 339 return err 340 } 341 return c.Wait() 342 } 343 344 // lookExtensions finds windows executable by its dir and path. 345 // It uses LookPath to try appropriate extensions. 346 // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. 347 func lookExtensions(path, dir string) (string, error) { 348 if filepath.Base(path) == path { 349 path = filepath.Join(".", path) 350 } 351 if dir == "" { 352 return LookPath(path) 353 } 354 if filepath.VolumeName(path) != "" { 355 return LookPath(path) 356 } 357 if len(path) > 1 && os.IsPathSeparator(path[0]) { 358 return LookPath(path) 359 } 360 dirandpath := filepath.Join(dir, path) 361 // We assume that LookPath will only add file extension. 362 lp, err := LookPath(dirandpath) 363 if err != nil { 364 return "", err 365 } 366 ext := strings.TrimPrefix(lp, dirandpath) 367 return path + ext, nil 368 } 369 370 // Start starts the specified command but does not wait for it to complete. 371 // 372 // If Start returns successfully, the c.Process field will be set. 373 // 374 // The Wait method will return the exit code and release associated resources 375 // once the command exits. 376 func (c *Cmd) Start() error { 377 if c.lookPathErr != nil { 378 c.closeDescriptors(c.closeAfterStart) 379 c.closeDescriptors(c.closeAfterWait) 380 return c.lookPathErr 381 } 382 if runtime.GOOS == "windows" { 383 lp, err := lookExtensions(c.Path, c.Dir) 384 if err != nil { 385 c.closeDescriptors(c.closeAfterStart) 386 c.closeDescriptors(c.closeAfterWait) 387 return err 388 } 389 c.Path = lp 390 } 391 if c.Process != nil { 392 return errors.New("exec: already started") 393 } 394 if c.ctx != nil { 395 select { 396 case <-c.ctx.Done(): 397 c.closeDescriptors(c.closeAfterStart) 398 c.closeDescriptors(c.closeAfterWait) 399 return c.ctx.Err() 400 default: 401 } 402 } 403 404 c.childFiles = make([]*os.File, 0, 3+len(c.ExtraFiles)) 405 type F func(*Cmd) (*os.File, error) 406 for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { 407 fd, err := setupFd(c) 408 if err != nil { 409 c.closeDescriptors(c.closeAfterStart) 410 c.closeDescriptors(c.closeAfterWait) 411 return err 412 } 413 c.childFiles = append(c.childFiles, fd) 414 } 415 c.childFiles = append(c.childFiles, c.ExtraFiles...) 416 417 envv, err := c.envv() 418 if err != nil { 419 return err 420 } 421 422 c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ 423 Dir: c.Dir, 424 Files: c.childFiles, 425 Env: addCriticalEnv(dedupEnv(envv)), 426 Sys: c.SysProcAttr, 427 }) 428 if err != nil { 429 c.closeDescriptors(c.closeAfterStart) 430 c.closeDescriptors(c.closeAfterWait) 431 return err 432 } 433 434 c.closeDescriptors(c.closeAfterStart) 435 436 // Don't allocate the channel unless there are goroutines to fire. 437 if len(c.goroutine) > 0 { 438 c.errch = make(chan error, len(c.goroutine)) 439 for _, fn := range c.goroutine { 440 go func(fn func() error) { 441 c.errch <- fn() 442 }(fn) 443 } 444 } 445 446 if c.ctx != nil { 447 c.waitDone = make(chan struct{}) 448 go func() { 449 select { 450 case <-c.ctx.Done(): 451 c.Process.Kill() 452 case <-c.waitDone: 453 } 454 }() 455 } 456 457 return nil 458 } 459 460 // An ExitError reports an unsuccessful exit by a command. 461 type ExitError struct { 462 *os.ProcessState 463 464 // Stderr holds a subset of the standard error output from the 465 // Cmd.Output method if standard error was not otherwise being 466 // collected. 467 // 468 // If the error output is long, Stderr may contain only a prefix 469 // and suffix of the output, with the middle replaced with 470 // text about the number of omitted bytes. 471 // 472 // Stderr is provided for debugging, for inclusion in error messages. 473 // Users with other needs should redirect Cmd.Stderr as needed. 474 Stderr []byte 475 } 476 477 func (e *ExitError) Error() string { 478 return e.ProcessState.String() 479 } 480 481 // Wait waits for the command to exit and waits for any copying to 482 // stdin or copying from stdout or stderr to complete. 483 // 484 // The command must have been started by Start. 485 // 486 // The returned error is nil if the command runs, has no problems 487 // copying stdin, stdout, and stderr, and exits with a zero exit 488 // status. 489 // 490 // If the command fails to run or doesn't complete successfully, the 491 // error is of type *ExitError. Other error types may be 492 // returned for I/O problems. 493 // 494 // If any of c.Stdin, c.Stdout or c.Stderr are not an *os.File, Wait also waits 495 // for the respective I/O loop copying to or from the process to complete. 496 // 497 // Wait releases any resources associated with the Cmd. 498 func (c *Cmd) Wait() error { 499 if c.Process == nil { 500 return errors.New("exec: not started") 501 } 502 if c.finished { 503 return errors.New("exec: Wait was already called") 504 } 505 c.finished = true 506 507 state, err := c.Process.Wait() 508 if c.waitDone != nil { 509 close(c.waitDone) 510 } 511 c.ProcessState = state 512 513 var copyError error 514 for range c.goroutine { 515 if err := <-c.errch; err != nil && copyError == nil { 516 copyError = err 517 } 518 } 519 520 c.closeDescriptors(c.closeAfterWait) 521 522 if err != nil { 523 return err 524 } else if !state.Success() { 525 return &ExitError{ProcessState: state} 526 } 527 528 return copyError 529 } 530 531 // Output runs the command and returns its standard output. 532 // Any returned error will usually be of type *ExitError. 533 // If c.Stderr was nil, Output populates ExitError.Stderr. 534 func (c *Cmd) Output() ([]byte, error) { 535 if c.Stdout != nil { 536 return nil, errors.New("exec: Stdout already set") 537 } 538 var stdout bytes.Buffer 539 c.Stdout = &stdout 540 541 captureErr := c.Stderr == nil 542 if captureErr { 543 c.Stderr = &prefixSuffixSaver{N: 32 << 10} 544 } 545 546 err := c.Run() 547 if err != nil && captureErr { 548 if ee, ok := err.(*ExitError); ok { 549 ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes() 550 } 551 } 552 return stdout.Bytes(), err 553 } 554 555 // CombinedOutput runs the command and returns its combined standard 556 // output and standard error. 557 func (c *Cmd) CombinedOutput() ([]byte, error) { 558 if c.Stdout != nil { 559 return nil, errors.New("exec: Stdout already set") 560 } 561 if c.Stderr != nil { 562 return nil, errors.New("exec: Stderr already set") 563 } 564 var b bytes.Buffer 565 c.Stdout = &b 566 c.Stderr = &b 567 err := c.Run() 568 return b.Bytes(), err 569 } 570 571 // StdinPipe returns a pipe that will be connected to the command's 572 // standard input when the command starts. 573 // The pipe will be closed automatically after Wait sees the command exit. 574 // A caller need only call Close to force the pipe to close sooner. 575 // For example, if the command being run will not exit until standard input 576 // is closed, the caller must close the pipe. 577 func (c *Cmd) StdinPipe() (io.WriteCloser, error) { 578 if c.Stdin != nil { 579 return nil, errors.New("exec: Stdin already set") 580 } 581 if c.Process != nil { 582 return nil, errors.New("exec: StdinPipe after process started") 583 } 584 pr, pw, err := os.Pipe() 585 if err != nil { 586 return nil, err 587 } 588 c.Stdin = pr 589 c.closeAfterStart = append(c.closeAfterStart, pr) 590 wc := &closeOnce{File: pw} 591 c.closeAfterWait = append(c.closeAfterWait, wc) 592 return wc, nil 593 } 594 595 type closeOnce struct { 596 *os.File 597 598 once sync.Once 599 err error 600 } 601 602 func (c *closeOnce) Close() error { 603 c.once.Do(c.close) 604 return c.err 605 } 606 607 func (c *closeOnce) close() { 608 c.err = c.File.Close() 609 } 610 611 // StdoutPipe returns a pipe that will be connected to the command's 612 // standard output when the command starts. 613 // 614 // Wait will close the pipe after seeing the command exit, so most callers 615 // need not close the pipe themselves. It is thus incorrect to call Wait 616 // before all reads from the pipe have completed. 617 // For the same reason, it is incorrect to call Run when using StdoutPipe. 618 // See the example for idiomatic usage. 619 func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { 620 if c.Stdout != nil { 621 return nil, errors.New("exec: Stdout already set") 622 } 623 if c.Process != nil { 624 return nil, errors.New("exec: StdoutPipe after process started") 625 } 626 pr, pw, err := os.Pipe() 627 if err != nil { 628 return nil, err 629 } 630 c.Stdout = pw 631 c.closeAfterStart = append(c.closeAfterStart, pw) 632 c.closeAfterWait = append(c.closeAfterWait, pr) 633 return pr, nil 634 } 635 636 // StderrPipe returns a pipe that will be connected to the command's 637 // standard error when the command starts. 638 // 639 // Wait will close the pipe after seeing the command exit, so most callers 640 // need not close the pipe themselves. It is thus incorrect to call Wait 641 // before all reads from the pipe have completed. 642 // For the same reason, it is incorrect to use Run when using StderrPipe. 643 // See the StdoutPipe example for idiomatic usage. 644 func (c *Cmd) StderrPipe() (io.ReadCloser, error) { 645 if c.Stderr != nil { 646 return nil, errors.New("exec: Stderr already set") 647 } 648 if c.Process != nil { 649 return nil, errors.New("exec: StderrPipe after process started") 650 } 651 pr, pw, err := os.Pipe() 652 if err != nil { 653 return nil, err 654 } 655 c.Stderr = pw 656 c.closeAfterStart = append(c.closeAfterStart, pw) 657 c.closeAfterWait = append(c.closeAfterWait, pr) 658 return pr, nil 659 } 660 661 // prefixSuffixSaver is an io.Writer which retains the first N bytes 662 // and the last N bytes written to it. The Bytes() methods reconstructs 663 // it with a pretty error message. 664 type prefixSuffixSaver struct { 665 N int // max size of prefix or suffix 666 prefix []byte 667 suffix []byte // ring buffer once len(suffix) == N 668 suffixOff int // offset to write into suffix 669 skipped int64 670 671 // TODO(bradfitz): we could keep one large []byte and use part of it for 672 // the prefix, reserve space for the '... Omitting N bytes ...' message, 673 // then the ring buffer suffix, and just rearrange the ring buffer 674 // suffix when Bytes() is called, but it doesn't seem worth it for 675 // now just for error messages. It's only ~64KB anyway. 676 } 677 678 func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) { 679 lenp := len(p) 680 p = w.fill(&w.prefix, p) 681 682 // Only keep the last w.N bytes of suffix data. 683 if overage := len(p) - w.N; overage > 0 { 684 p = p[overage:] 685 w.skipped += int64(overage) 686 } 687 p = w.fill(&w.suffix, p) 688 689 // w.suffix is full now if p is non-empty. Overwrite it in a circle. 690 for len(p) > 0 { // 0, 1, or 2 iterations. 691 n := copy(w.suffix[w.suffixOff:], p) 692 p = p[n:] 693 w.skipped += int64(n) 694 w.suffixOff += n 695 if w.suffixOff == w.N { 696 w.suffixOff = 0 697 } 698 } 699 return lenp, nil 700 } 701 702 // fill appends up to len(p) bytes of p to *dst, such that *dst does not 703 // grow larger than w.N. It returns the un-appended suffix of p. 704 func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) { 705 if remain := w.N - len(*dst); remain > 0 { 706 add := minInt(len(p), remain) 707 *dst = append(*dst, p[:add]...) 708 p = p[add:] 709 } 710 return p 711 } 712 713 func (w *prefixSuffixSaver) Bytes() []byte { 714 if w.suffix == nil { 715 return w.prefix 716 } 717 if w.skipped == 0 { 718 return append(w.prefix, w.suffix...) 719 } 720 var buf bytes.Buffer 721 buf.Grow(len(w.prefix) + len(w.suffix) + 50) 722 buf.Write(w.prefix) 723 buf.WriteString("\n... omitting ") 724 buf.WriteString(strconv.FormatInt(w.skipped, 10)) 725 buf.WriteString(" bytes ...\n") 726 buf.Write(w.suffix[w.suffixOff:]) 727 buf.Write(w.suffix[:w.suffixOff]) 728 return buf.Bytes() 729 } 730 731 func minInt(a, b int) int { 732 if a < b { 733 return a 734 } 735 return b 736 } 737 738 // dedupEnv returns a copy of env with any duplicates removed, in favor of 739 // later values. 740 // Items not of the normal environment "key=value" form are preserved unchanged. 741 func dedupEnv(env []string) []string { 742 return dedupEnvCase(runtime.GOOS == "windows", env) 743 } 744 745 // dedupEnvCase is dedupEnv with a case option for testing. 746 // If caseInsensitive is true, the case of keys is ignored. 747 func dedupEnvCase(caseInsensitive bool, env []string) []string { 748 out := make([]string, 0, len(env)) 749 saw := make(map[string]int, len(env)) // key => index into out 750 for _, kv := range env { 751 eq := strings.Index(kv, "=") 752 if eq < 0 { 753 out = append(out, kv) 754 continue 755 } 756 k := kv[:eq] 757 if caseInsensitive { 758 k = strings.ToLower(k) 759 } 760 if dupIdx, isDup := saw[k]; isDup { 761 out[dupIdx] = kv 762 continue 763 } 764 saw[k] = len(out) 765 out = append(out, kv) 766 } 767 return out 768 } 769 770 // addCriticalEnv adds any critical environment variables that are required 771 // (or at least almost always required) on the operating system. 772 // Currently this is only used for Windows. 773 func addCriticalEnv(env []string) []string { 774 if runtime.GOOS != "windows" { 775 return env 776 } 777 for _, kv := range env { 778 eq := strings.Index(kv, "=") 779 if eq < 0 { 780 continue 781 } 782 k := kv[:eq] 783 if strings.EqualFold(k, "SYSTEMROOT") { 784 // We already have it. 785 return env 786 } 787 } 788 return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT")) 789 }