os/exec: move platform-specific LookPath docs to a common comment

We have four different LookPath variations (unix, windows, plan9, wasm),
each with slightly different doc comments. Unify the documentation and
move it to a single, common LookPath.

Change-Id: I56bae57e80887a73ef0f6933258ee0a48dbccdcf
Reviewed-on: https://go-review.googlesource.com/c/go/+/734320
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Damien Neil 2026-01-06 15:57:00 -08:00
parent b7e6d8b923
commit c16402d15b
5 changed files with 41 additions and 38 deletions

30
src/os/exec/lookpath.go Normal file
View file

@ -0,0 +1,30 @@
// Copyright 2026 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
// LookPath searches for an executable named file in the current path,
// following the conventions of the host operating system.
// If file contains a slash, it is tried directly and the default path is not consulted.
// Otherwise, on success the result is an absolute path.
//
// LookPath returns an error satisfying [errors.Is](err, [ErrDot])
// if the resolved path is relative to the current directory.
// See the package documentation for more details.
//
// LookPath looks for an executable named file in the
// directories named by the PATH environment variable,
// except as described below.
//
// - On Windows, the file must have an extension named by
// the PATHEXT environment variable.
// When PATHEXT is unset, the file must have
// a ".com", ".exe", ".bat", or ".cmd" extension.
// - On Plan 9, LookPath consults the path environment variable.
// If file begins with "/", "#", "./", or "../", it is tried
// directly and the path is not consulted.
// - On Wasm, LookPath always returns an error.
func LookPath(file string) (string, error) {
return lookPath(file)
}

View file

@ -26,16 +26,7 @@ func findExecutable(file string) error {
return fs.ErrPermission
}
// LookPath searches for an executable named file in the
// directories named by the path environment variable.
// If file begins with "/", "#", "./", or "../", it is tried
// directly and the path is not consulted.
// On success, the result is an absolute path.
//
// In older versions of Go, LookPath could return a path relative to the current directory.
// As of Go 1.19, LookPath will instead return that path along with an error satisfying
// [errors.Is](err, [ErrDot]). See the package documentation for more details.
func LookPath(file string) (string, error) {
func lookPath(file string) (string, error) {
if err := validateLookPath(filepath.Clean(file)); err != nil {
return "", &Error{file, err}
}

View file

@ -41,15 +41,7 @@ func findExecutable(file string) error {
return fs.ErrPermission
}
// LookPath searches for an executable named file in the
// directories named by the PATH environment variable.
// If file contains a slash, it is tried directly and the PATH is not consulted.
// Otherwise, on success, the result is an absolute path.
//
// In older versions of Go, LookPath could return a path relative to the current directory.
// As of Go 1.19, LookPath will instead return that path along with an error satisfying
// [errors.Is](err, [ErrDot]). See the package documentation for more details.
func LookPath(file string) (string, error) {
func lookPath(file string) (string, error) {
// NOTE(rsc): I wish we could use the Plan 9 behavior here
// (only bypass the path if file begins with / or ./ or ../)
// but that would not match all the Unix shells.

View file

@ -17,7 +17,7 @@ var ErrNotFound = errors.New("executable file not found in $PATH")
// directories named by the PATH environment variable.
// If file contains a slash, it is tried directly and the PATH is not consulted.
// The result may be an absolute path or a path relative to the current directory.
func LookPath(file string) (string, error) {
func lookPath(file string) (string, error) {
// Wasm can not execute processes, so act as if there are no executables at all.
return "", &Error{file, ErrNotFound}
}

View file

@ -56,22 +56,12 @@ func findExecutable(file string, exts []string) (string, error) {
return "", ErrNotFound
}
// LookPath searches for an executable named file in the
// directories named by the PATH environment variable.
// LookPath also uses PATHEXT environment variable to match
// a suitable candidate.
// If file contains a slash, it is tried directly and the PATH is not consulted.
// Otherwise, on success, the result is an absolute path.
//
// In older versions of Go, LookPath could return a path relative to the current directory.
// As of Go 1.19, LookPath will instead return that path along with an error satisfying
// [errors.Is](err, [ErrDot]). See the package documentation for more details.
func LookPath(file string) (string, error) {
func lookPath(file string) (string, error) {
if err := validateLookPath(file); err != nil {
return "", &Error{file, err}
}
return lookPath(file, pathExt())
return lookPathExts(file, pathExt())
}
// lookExtensions finds windows executable by its dir and path.
@ -101,17 +91,17 @@ func lookExtensions(path, dir string) (string, error) {
}
}
if dir == "" {
return lookPath(path, exts)
return lookPathExts(path, exts)
}
if filepath.VolumeName(path) != "" {
return lookPath(path, exts)
return lookPathExts(path, exts)
}
if len(path) > 1 && os.IsPathSeparator(path[0]) {
return lookPath(path, exts)
return lookPathExts(path, exts)
}
dirandpath := filepath.Join(dir, path)
// We assume that LookPath will only add file extension.
lp, err := lookPath(dirandpath, exts)
lp, err := lookPathExts(dirandpath, exts)
if err != nil {
return "", err
}
@ -138,8 +128,8 @@ func pathExt() []string {
return exts
}
// lookPath implements LookPath for the given PATHEXT list.
func lookPath(file string, exts []string) (string, error) {
// lookPathExts implements LookPath for the given PATHEXT list.
func lookPathExts(file string, exts []string) (string, error) {
if strings.ContainsAny(file, `:\/`) {
f, err := findExecutable(file, exts)
if err == nil {