https://go.dev/doc/go1.22#os/exec includes:
On Windows,
Command
andCmd.Start
no longer callLookPath
if the path to the executable is already absolute and has an executable file extension.
I don't quite understand what criteria is used for determining whether a path has an executable file extension. Which extensions are included? Is the PATHEXT
environment variable involved?
That said, I've narrowed down a following behavior change between Go 1.21 and 1.22 that I'm not sure if it's working as intended, so reporting it for investigation.
Consider the output of the following Go program on a Windows machine that has an executable file at the path "C:\Program Files\Go\bin\gofmt.exe", in a roughly default environment (i.e., PATHEXT
is not modified):
package main
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func main() {
if _, err := os.Stat(`C:\Program Files\Go\bin\gofmt.exe`); err != nil {
fmt.Println("returning early; if gofmt.exe doesn't exist the rest of the output will be misleading")
return
} else if !strings.Contains(os.Getenv("PATHEXT"), ".EXE") {
fmt.Println("returning early; if .exe isn't included in PATHEXT the rest of the output will be misleading")
return
}
cmdViaCommand := exec.Command(`C:\Program Files\Go\bin\gofmt`)
cmdManualPath := &exec.Cmd{
Path: `C:\Program Files\Go\bin\gofmt`,
Args: []string{`C:\Program Files\Go\bin\gofmt`},
}
diff := cmp.Diff(cmdViaCommand, cmdManualPath, cmpopts.IgnoreUnexported(exec.Cmd{}))
if diff == "" {
diff = "(no diff)\n"
}
fmt.Printf("diff (-cmdViaCommand +cmdManualPath):\n%s\n", diff)
err := cmdManualPath.Run()
fmt.Println("err:", err)
}
When running it using Go 1.21.8, the output is:
$ go run .
diff (-cmdViaCommand +cmdManualPath):
(no diff)
err: <nil>
But when running it with Go 1.22.1:
$ go run .
diff (-cmdViaCommand +cmdManualPath):
&exec.Cmd{
- Path: `C:\Program Files\Go\bin\gofmt.exe`,
+ Path: `C:\Program Files\Go\bin\gofmt`,
Args: {`C:\Program Files\Go\bin\gofmt`},
Env: nil,
... // 8 ignored and 11 identical fields
}
err: fork/exec C:\Program Files\Go\bin\gofmt: The system cannot find the file specified.
Not having to manually add ".exe" to the path and instead relying on the PATHEXT
mechanism is very convenient when writing multi-platform Go programs, since it permits there not to be special cases for one of the GOOS values.
In that context, it seems there's no change in behavior when using exec.Command
to create a *exec.Cmd
and then calling Cmd.Start
on it. But when creating it manually, Go 1.21 would use PATHEXT
compensate for Cmd.Path
missing a ".exe" suffix, whereas Go 1.22 doesn't. I can't quite tell from os/exec documentation (or the seemingly relevant release note) if this is a bug fix or a bug.
CC @golang/windows.
Comment From: rsc
@gopherbot please backport go1.22
Comment From: gopherbot
Backport issue(s) opened: #66598 (for 1.22).
Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.
Comment From: gopherbot
Change https://go.dev/cl/575255 mentions this issue: os/exec: revert "avoid calling LookPath in cmd.Start for resolved paths"
Comment From: gopherbot
Change https://go.dev/cl/575275 mentions this issue: os/exec: resolve extension in Cmd.Start if Command wasn't used
Comment From: cagedmantis
This issue was reviewed in the release meeting. Is there any status update to report on this issue?
Comment From: joedian
@dmitshur I see the CL was abandoned, is this issue also obsolete?
Comment From: dmitshur
@joedian It was a draft CL associated with a review comment. Russ has go.dev/cl/575255 which is still open but needs attention.
Comment From: gopherbot
Change https://go.dev/cl/581695 mentions this issue: os/exec: not add a suffix to Cmd.Path
Comment From: cagedmantis
@rsc Is there is status update on this issue? There is a CL that is ready for review. This came up in the release meeting.
Comment From: mdempsky
Ping? Is this still a 1.23 release blocker? It seems like it's an issue present in 1.22, and so it merits backporting either way.
Comment From: rsc
I'll take a look later today.
Comment From: gopherbot
Change https://go.dev/cl/589016 mentions this issue: os/exec: add new test
Comment From: gopherbot
Change https://go.dev/cl/594495 mentions this issue: [release-branch.go1.22] os/exec: on Windows look for extensions in Run if not already done
Comment From: gopherbot
Change https://go.dev/cl/596875 mentions this issue: os/exec: only use cachedLookExtensions if Cmd.Path is unmodified
Comment From: gopherbot
Change https://go.dev/cl/596976 mentions this issue: [release-branch.go1.22] os/exec: only use cachedLookExtensions if Cmd.Path is unmodified