go exec.Command windows 参数引号转义问题

发布时间 2023-05-30 17:18:27作者: DirWangK

Go在windows上调用本地进程传参时的一个天坑

Golang

go在windows上exec.Command调用本地进程在传参的时候有一个天坑,举个栗子来说正常来说一般代码会这么写

 cmdLine := "notepad.exe " + `"D:\Program Files\Notepad++\session.xml"`
 cmd := exec.Command("cmd.exe", "/c", cmdLine)
 err := cmd.Run()

我们期望在拉起notepad的时候是会正常解析参数并打开xml文件的,但是,你可以尝试运行一下这段代码,结果并不是我们想的这样!

notepad在打开的时候会报一个非法路径,这是怎么回事呢?打开processexp看一下传入的参数如下

notepad.exe "D:\Program Files\Notepad++\session.xml"
看到在双引号中强制加入了转义符,一开始我以为这是string里强制加入的,想到这做法也太奇怪了,强制转string为byte[]后,一个一个对字符发现原字符串中根本就没有出现转义符,搜遍国内外只有提问的人,却没找到有效的解决办法。

想了一下,那这问题必然就出在Command解析中了,查看源码中有一段说明

On Windows, processes receive the whole command line as a single string and do their own parsing. Command combines and quotes Args into a command line string with an algorithm compatible with applications using CommandLineToArgvW (which is the most common way). Notable exceptions are msiexec.exe and cmd.exe (and thus, all batch files), which have a different unquoting algorithm. In these or other similar cases, you can do the quoting yourself and provide the full command line in SysProcAttr.CmdLine, leaving Args empty.

也就是说,针对cmd参数加的引号参数会有不同的逻辑,必须在SysProcAttr.CmdLine中写入原始参数了,但是Args留空,又会导致SysProcAttr值为nil,所以简单赋值也是不行的,那么正确的代码实现如下

 cmdLine := "notepad.exe " + `"D:\Program Files\Notepad++\session.xml"`
 cmd := exec.Command("cmd.exe")
 cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: "/c " + cmdLine}
 err := cmd.Run()

运行一下,果然notepad正确打开了文件

原文:https://oser.space/post/gocmd/