被 golang 坑了一下午, win 平台无法正常调用外部程序看这里。

2022-04-08 17:33:43 +08:00
 zeronofreya

很简单的调用:

cmd := exec.Command( "TeraCopy.exe", fmt.Sprintf(`*"%s" "%s"`, copyPaths, targetDir) )
err := cmd.Start()

报错:

---------------------------
 TeraCopy - Error
---------------------------
File not found:
\e:\**\tc.tmp \E:\**\b\
---------------------------
确定   
---------------------------

??? 啥玩意 ???

本着出了问题先找自身原因的混帐话优良传统,控制台与 TeraCopy 软件试了各种参数,都正常……

感觉是转义出了问题,谷歌了半天,除了复制粘贴就没别的了。

米田共里淘金终于发现了一片文章:Go 在 windows 上调用本地进程传参时的一个天坑

MD ,最终 tm 还是 go 的问题,一直不敢往那想,属实被喷怕了。 摘抄一下:

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 := fmt.Sprintf(`copy *"%s" "%s"`, copyPaths, targetDir)
cmd := exec.Command("TeraCopy.exe")
cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: "/c " + cmdLine}
err := cmd.Start()

解决。

学习 go 一段时间,觉得它的开发者很矛盾,比如三元运算符,很多人都想要它,但官方却以语法统一、可能会导致阅读困难之类的理由推脱。 但

return r ? true : false

不比你 if 要整洁易读? 对多字节的处理也很费劲,我到现在不知道怎样查找某个中文的位置,IndexRune 报错,也没谷歌到答案

fmt.Println(strings.IndexRune("学习", "习"))


Error
./prog.go:9:42: cannot use "习" (untyped string constant) as rune value in argument to strings.IndexRune

是不是有人要说,爱用用,不用滚呢?

6406 次点击
所在节点    程序员
65 条回复
haochen2
2022-04-08 17:39:26 +08:00
strings.IndexRune 建议好好看看函数签名,第二个参数是 rune 类型,你给了个字符串
darrh00
2022-04-08 17:45:38 +08:00
strings.IndexRune("学习", '习')
ikaros
2022-04-08 17:46:42 +08:00
啊? 第一个问题报错 file not found 应该很好一步步排查吧; 第二个错误也说了 cannot use "习" (untyped string constant) as rune value in argument to strings.IndexRune ,而且这个是编译期的问题,编辑器就能提示出来的吧;顺带我也觉得 go 有点烂,转 rust 吧
haochen2
2022-04-08 17:47:13 +08:00
go 标准库实现的质量非常高,希望能抱着学习的心态去探索她它
keepeye
2022-04-08 17:52:29 +08:00
哈哈 你之前用的啥? java 吗?
fishCatcher
2022-04-08 17:55:56 +08:00
来跟我一个字一个字读:cannot use untyped string constant as rune value in argument
Mohanson
2022-04-08 17:57:41 +08:00
exec.Command 每一个 args 都要做一个参数传的, 正确写法是 `exec.Command("TeraCopy.exe", copyPaths, targetDir)`

另外真的很烦你这种人...
v2kt
2022-04-08 17:58:39 +08:00
cannot use untyped string constant as rune value in argument
ck65
2022-04-08 17:59:31 +08:00
对,会有人说的。
Trim21
2022-04-08 18:04:30 +08:00
exec.Command 的 args 是直接传进去的,不需要用 fmt 再拼接一下,这地方不是 shell 还会解析你的命令行。(这个问题在其他语言其实也有类似的)

string 和 rune 不是一个东西。
sqfphoenix
2022-04-08 18:04:42 +08:00
cmd := exec.Command("notepad.exe", filepath.Join("D:/", "metrics.txt"))
使用 filepath 很难吗
使用 cmd.String()输出看看自己的命令很难吗
看一下 strings 的源码很难吗
lance6716
2022-04-08 18:14:36 +08:00
无力吐槽,再说两个

return r ? true : false 跟 return r 有什么区别?

调用 exec.Command 之前不会看一眼他的函数注释?
ScepterZ
2022-04-08 18:16:46 +08:00
实习那年遇到过这个问题,后来发现是每个 args 要单独传,不能自己拼起来

最后那个,分不清字符和字符串实在不是语言的问题
CRVV
2022-04-08 18:18:54 +08:00
这个传参数的问题实际上是 Windows 的问题,我大概解释一下

首先,大家通常理解的参数,是一个数组。
C 语言里面会写 int main( int argc, char* argv[] ),Java 会写 public static void main(String[] args),参数都是数组。
所以 Go 的 exec.Command 是 func Command(name string, arg ...string) *Cmd 。arg 也是数组,写 Command("echo", "a", "b", "c") 就是传了 3 个参数给 echo 。

但是,Windows 里面的参数是一个字符串,也就是 Go 的文档里面说的 “On Windows, processes receive the whole command line as a single string”
Windows 里面的程序在接收到这个大字符串以后,再自己把它拆成数组,得到大家通常理解的一串参数。
问题在于,Windows 不同的程序有不同的方法来拆这个字符串,所以拆出来的数组也不一样。
这里 Go 的行为是说,如果你在 Command 里传了一个数组的参数,那么 Go 就按照 Windows 通常的拆法把这些参数拼起来,但是 Windows 上的 cmd.exe 和 msiexec.exe 的拆法并不是那个通常的拆法,所以会出问题。如果你要调用的程序是 cmd.exe ,那么 Go 拼字符串的算法就是错的,请自己把字符串拼好了放到 SysProcAttr 里面。

但是楼主调用的程序不是 cmd.exe ,也就是说楼主遇到的问题根本不是这个问题。

我能看出来的问题,首先是一开始楼主少写了一个 copy ,然后是把两个参数拼到了一个参数里面,那当然不能用了。
这个问题和 Go 和 Windows 都没有关系,纯粹是楼主自己的问题。

把这个事讲完,把两个参数拼到一个参数里面的意思是

cat a b 是说把 a 和 b 两个文件的内容打出来
cat "a b" 是说有一个文件的文件名是 a 空格 b ,把这一个文件的内容打出来,如果你想要的是前一种,这样写就会报错说找不到文件
cassyfar
2022-04-08 18:21:13 +08:00
这种水平也来杠?
ChrisFreeMan
2022-04-08 18:23:55 +08:00
go 的话题真活跃啊,问个 swift 的问题半天没人理
thevita
2022-04-08 18:29:41 +08:00
比较适合些 PHP
----
没有看不起 PHP 的意思,PHP 是最好的语言
ysc3839
2022-04-08 18:42:43 +08:00
@CRVV 补充一下,类 Unix 系统传递的参数就是字符串数组,调用方怎么传递的,接收方可以原样接收到,不需要考虑引号、转义之类的问题。
yyf1234
2022-04-08 18:47:24 +08:00
排错能力不太行呀兄弟
0o0O0o0O0o
2022-04-08 18:55:35 +08:00
我也至今没搞懂 rune ,所以会避免用…不过我明白这是因为自己菜,菜是因为自己懒不去读文档

我偶尔用 go 写一些 windows 的小工具,遇到过一些真正的坑,不过我对平台相关或是 cgo 相关的坑都比较宽容…

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/845764

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX