英文:
In Go, why does exec.Command() fail but os.StartProcess() succeed launching "winget.exe"?
问题
exec.Command()
可以用于执行C:\Windows\System32\notepad.exe
,但无法执行C:\Users\<username>\AppData\Local\Microsoft\WindowsApps\winget.exe
,并显示以下错误信息:exec: "C:\Users\<username>\AppData\Local\Microsoft\WindowsApps\winget.exe": file does not exist
。然而,os.StartProcess()
可以执行C:\Users\<username>\AppData\Local\Microsoft\WindowsApps\winget.exe
。
有人可以告诉我为什么吗?
这段代码无法正常工作,winget.exe
没有被启动。
wingetPath := filepath.Join(os.Getenv("LOCALAPPDATA"),
"Microsoft\\WindowsApps\\winget.exe")
cmd := exec.Command(wingetPath, "--version")
err := cmd.Start()
fmt.Println(err)
// exec: "C:\Users\<username>\AppData\Local\Microsoft\WindowsApps\winget.exe": file does not exist
但是这段代码可以正常工作:
wingetPath := filepath.Join(os.Getenv("LOCALAPPDATA"),
"Microsoft\\WindowsApps\\winget.exe")
procAttr := new(os.ProcAttr)
procAttr.Files = []*os.File{nil, nil, nil}
// The argv slice will become os.Args in the new process,
// so it normally starts with the program name
_, err := os.StartProcess(wingetPath, []string{wingetPath, "--version"}, procAttr)
fmt.Println(err)
// <nil>
Go版本:
> go version
go version go1.18 windows/amd64
英文:
exec.Command()
works for executingC:\Windows\System32\notepad.exe
- But
exec.Command()
doesn't work for executingC:\Users\<username>\AppData\Local\Microsoft\WindowsApps\winget.exe
. Fails with the error message:exec: "C:\\Users\\<username>\\AppData\\Local\\Microsoft\\WindowsApps\\winget.exe": file does not exist
- However,
os.StartProcess()
works for executingC:\Users\<username>\AppData\Local\Microsoft\WindowsApps\winget.exe
Can someone tell me why?
This code fragment does not work. winget.exe
isn't launched.
wingetPath := filepath.Join(os.Getenv("LOCALAPPDATA"),
"Microsoft\\WindowsApps\\winget.exe")
cmd := exec.Command(wingetPath, "--version")
err := cmd.Start()
fmt.Println(err)
// exec: "C:\\Users\\<username>\\AppData\\Local\\Microsoft\\WindowsApps\\winget.exe": file does not exist
But this works:
wingetPath := filepath.Join(os.Getenv("LOCALAPPDATA"),
"Microsoft\\WindowsApps\\winget.exe")
procAttr := new(os.ProcAttr)
procAttr.Files = []*os.File{nil, nil, nil}
// The argv slice will become os.Args in the new process,
// so it normally starts with the program name
_, err := os.StartProcess(wingetPath, []string{wingetPath, "--version"}, procAttr)
fmt.Println(err)
// <nil>
Go version:
> go version
go version go1.18 windows/amd64
答案1
得分: 6
Golang中的Bug
显然,这是Go
在Windows实现中的一个bug,并且已经在GitHub上多次提交了 - 我找到的最早的问题是几年前提交的这个问题。
这个bug是由于exec.Command()
在内部使用了os.Stat()
,而os.Stat()
无法正确读取带有重解析点的文件所导致的。而os.Lstat()
可以。
Windows Store应用程序使用应用程序执行别名,它们实际上是带有重解析点的零字节文件。这个文章有一些额外的细节。
解决方法
- 解决方法是使用
os.StartProcess()
- 一个较低级别的API,使用起来可能有些麻烦,特别是与os.Exec()
相比。
<br />重要提示:在os.StartProcess()
中,argv切片将成为新进程中的os.Args
,因此通常应将程序名称作为第一个参数传递:
wingetPath := filepath.Join(os.Getenv("LOCALAPPDATA"),
"Microsoft\\WindowsApps\\winget.exe")
procAttr := new(os.ProcAttr)
procAttr.Files = []*os.File{nil, nil, nil}
/*
要重定向IO,请根据需要传递stdin、stdout、stderr
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
*/
args := []string{"install", "git.git"}
// argv切片将成为新进程中的os.Args,因此通常以程序名称开头
proc, err := os.StartProcess(wingetPath,
append([]string{wingetPath}, args...), procAttr)
fmt.Println(err) // nil
英文:
Bug in Golang
So apparently this is a bug in the Windows implementation of Go
and has been filed on GitHub many times - the oldest I could find is this issue which was filed years ago.
The bug is caused by the fact that exec.Command()
internally uses os.Stat()
which does not read files with reparse points correctly. os.Lstat()
can.
Windows Store apps use App Execution Aliases, which are essentially zero-byte files with reparse points. This post has some additional details.
Workarounds
- Workaround is to use
os.StartProces()
- a lower level API which can be a bit painful to use especially when compared toos.Exec()
.
<br />Important: Inos.StartProcess()
, the argv slice will becomeos.Args
in the new process, so you should normally pass the program name as the first argument:
wingetPath := filepath.Join(os.Getenv("LOCALAPPDATA"),
"Microsoft\\WindowsApps\\winget.exe")
procAttr := new(os.ProcAttr)
procAttr.Files = []*os.File{nil, nil, nil}
/*
To redirect IO, pass in stdin, stdout, stderr as required
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
*/
args = []string { "install", "git.git" }
// The argv slice will become os.Args in the new process,
// so it normally starts with the program name
proc, err := os.StartProcess(wingetPath,
append([]string{wingetPath}, arg...), procAttr)
fmt.Println(err) // nil
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论