aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/os/exec/exec.go
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2014-02-03 16:32:13 -0500
committerBrad Fitzpatrick <bradfitz@golang.org>2014-02-03 16:32:13 -0500
commite6d8bfe218a5f387d7aceddcaee5067a59181838 (patch)
tree965fc1516ab382fb6d92c90bb8161d6c78df0f04 /src/pkg/os/exec/exec.go
parent4723308ff5ab437d2b0892e7cdac86d490495a9b (diff)
downloadgo-e6d8bfe218a5f387d7aceddcaee5067a59181838.tar.xz
os/exec: fix Command with relative paths
Command was (and is) documented like: "If name contains no path separators, Command uses LookPath to resolve the path to a complete name if possible. Otherwise it uses name directly." But that wasn't true. It always did LookPath, and then set a sticky error that the user couldn't unset. And then if cmd.Dir was changed, Start would still fail due to the earlier sticky error being set. This keeps LookPath in the same place as before (so no user visible changes in cmd.Path after Command), but only does it when the documentation says it will happen. Also, clarify the docs about a relative Dir path. No change in any existing behavior, except using Command is now possible with relative paths. Previously it only worked if you built the *Cmd by hand. Fixes #7228 LGTM=iant R=iant CC=adg, golang-codereviews https://golang.org/cl/59580044
Diffstat (limited to 'src/pkg/os/exec/exec.go')
-rw-r--r--src/pkg/os/exec/exec.go40
1 files changed, 26 insertions, 14 deletions
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go
index 491cc242bb..ea4f692a31 100644
--- a/src/pkg/os/exec/exec.go
+++ b/src/pkg/os/exec/exec.go
@@ -33,7 +33,8 @@ 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.
+ // value. If Path is relative, it is evaluated relative
+ // to Dir.
Path string
// Args holds command line arguments, including the command as Args[0].
@@ -84,7 +85,7 @@ type Cmd struct {
// available after a call to Wait or Run.
ProcessState *os.ProcessState
- err error // last error (from LookPath, stdin, stdout, stderr)
+ lookPathErr error // LookPath error, if any.
finished bool // when Wait was called
childFiles []*os.File
closeAfterStart []io.Closer
@@ -96,8 +97,7 @@ type Cmd struct {
// Command returns the Cmd struct to execute the named program with
// the given arguments.
//
-// It sets Path and Args in the returned structure and zeroes the
-// other fields.
+// 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
@@ -107,19 +107,31 @@ type Cmd struct {
// 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 {
- aname, err := LookPath(name)
- if err != nil {
- aname = name
- }
- return &Cmd{
- Path: aname,
+ cmd := &Cmd{
+ Path: name,
Args: append([]string{name}, arg...),
- err: err,
}
+ if !containsPathSeparator(name) {
+ if lp, err := LookPath(name); err != nil {
+ cmd.lookPathErr = err
+ } else {
+ cmd.Path = lp
+ }
+ }
+ return cmd
+}
+
+func containsPathSeparator(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if os.IsPathSeparator(s[i]) {
+ return true
+ }
+ }
+ return false
}
// interfaceEqual protects against panics from doing equality tests on
-// two interfaces with non-comparable underlying types
+// two interfaces with non-comparable underlying types.
func interfaceEqual(a, b interface{}) bool {
defer func() {
recover()
@@ -235,10 +247,10 @@ func (c *Cmd) Run() error {
// Start starts the specified command but does not wait for it to complete.
func (c *Cmd) Start() error {
- if c.err != nil {
+ if c.lookPathErr != nil {
c.closeDescriptors(c.closeAfterStart)
c.closeDescriptors(c.closeAfterWait)
- return c.err
+ return c.lookPathErr
}
if c.Process != nil {
return errors.New("exec: already started")