func newCmd(sh *Shell, vars map[string]string, name string, args ...string) (*Cmd, error) { // Mimics https://golang.org/src/os/exec/exec.go Command. if filepath.Base(name) == name { lp, err := lookpath.Look(sh.Vars, name) if err != nil { return nil, fmt.Errorf("gosh: failed to locate executable: %s", name) } name = lp } return newCmdInternal(sh, vars, name, args) }
// execute executes the binary pointed to by the given path using the given // arguments and options. If the wait flag is set, the function waits for the // completion of the binary and the timeout value can optionally specify for // how long should the function wait before timing out. func (e *executor) execute(wait bool, timeout time.Duration, opts opts, path string, args ...string) (*exec.Cmd, error) { e.increaseIndent() defer e.decreaseIndent() // Check if <path> identifies a binary in the PATH environment // variable of the opts.Env. if binary, err := lookpath.Look(opts.env, path); err == nil { // If so, make sure to execute this binary. This step // enables us to "shadow" binaries included in the // PATH environment variable of the host OS (which // would be otherwise used to lookup <path>). // // This mechanism is used instead of modifying the // PATH environment variable of the host OS as the // latter is not thread-safe. path = binary } command := exec.Command(path, args...) command.Dir = opts.dir command.Stdin = opts.stdin command.Stdout = opts.stdout command.Stderr = opts.stderr command.Env = envvar.MapToSlice(opts.env) if out := e.verboseStdout(opts); out != ioutil.Discard { args := []string{} for _, arg := range command.Args { // Quote any arguments that contain '"', ''', '|', or ' '. if strings.IndexAny(arg, "\"' |") != -1 { args = append(args, strconv.Quote(arg)) } else { args = append(args, arg) } } e.printf(out, strings.Replace(strings.Join(args, " "), "%", "%%", -1)) } var err error switch { case !wait: err = command.Start() e.printf(e.verboseStdout(opts), okOrFailed(err)) case timeout == 0: err = command.Run() e.printf(e.verboseStdout(opts), okOrFailed(err)) default: err = e.timedCommand(timeout, opts, command) // Verbose output handled in timedCommand. } return command, err }
func TestLook(t *testing.T) { tmpDir, cleanup := initTmpDir(t) defer cleanup() dirA, dirB := mkdir(t, tmpDir, "a"), mkdir(t, tmpDir, "b") aFoo, aBar := mkfile(t, dirA, "foo", 0755), mkfile(t, dirA, "bar", 0755) bBar, bBaz := mkfile(t, dirB, "bar", 0755), mkfile(t, dirB, "baz", 0755) aExe, bExe := mkfile(t, dirA, "exe", 0644), mkfile(t, dirB, "exe", 0755) tests := []struct { Env map[string]string Name string Want string }{ {nil, "", ""}, {nil, "foo", ""}, {pathEnv(dirA), "foo", aFoo}, {pathEnv(dirA), "bar", aBar}, {pathEnv(dirA), "baz", ""}, {pathEnv(dirB), "foo", ""}, {pathEnv(dirB), "bar", bBar}, {pathEnv(dirB), "baz", bBaz}, {pathEnv(dirA, dirB), "foo", aFoo}, {pathEnv(dirA, dirB), "bar", aBar}, {pathEnv(dirA, dirB), "baz", bBaz}, // Make sure we find bExe, since aExe isn't executable. {pathEnv(dirA, dirB), "exe", bExe}, // Absolute name lookups. {nil, dirA, ""}, {nil, dirB, ""}, {nil, aFoo, aFoo}, {nil, aBar, aBar}, {nil, bBar, bBar}, {nil, bBaz, bBaz}, {nil, aExe, ""}, {nil, bExe, bExe}, } for _, test := range tests { hdr := fmt.Sprintf("env=%v name=%v", test.Env, test.Name) look, err := lookpath.Look(test.Env, test.Name) if got, want := look, test.Want; got != want { t.Errorf("%s got %v, want %v", hdr, got, want) } if (look == "") == (err == nil) { t.Errorf("%s got mismatched look=%v err=%v", hdr, look, err) } if err != nil && !isNotFoundError(err, test.Name) { t.Errorf("%s got wrong error %v", hdr, err) } } }