Golang中exec.Command与Python交互的异常问题探讨
Golang中exec.Command与Python交互的异常问题探讨 大家好,希望你们今天过得愉快。
最近,我遇到了近期见过的最奇怪的软件交互之一,我非常确定我没有做错任何事情(多次检查过,也请同行审查,看是否遗漏了什么),所以我想分享一下,以防有人遇到同样的问题,或者可能有更好的解释!
简而言之,当使用Go的exec.Command调用一个使用argparse库解析接收参数的Python脚本时,出于某种原因,通过查看sys.argv变量,参数被正确接收了,但使用argparse的parse_arguments函数解析时,参数被误解了,特别是在使用别名的简写标志时,它添加了一个原本不存在的空格。
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("./test.py", "-p /some/path")
fmt.Println(cmd.Args)
output, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("Error: %s", err)
}
fmt.Printf("\nOutput received: \n %s", output)
}
然后是Python代码:
#!/usr/bin/python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--path', '-p', dest='path')
args = parser.parse_args()
print(f"The path:>>{args.path}<<")
正如你将看到的,当使用Go运行程序时,它会在参数中添加一个空格,但从终端执行时则不会。
旁注:请确保将test.py设置为可执行文件,因为我们将其当作可执行文件处理,使用chmod +x test.py。
从Go运行:
$ go run main.go
从终端运行:
$ ./test.py -p /some/path
更多关于Golang中exec.Command与Python交互的异常问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你抢先一步了! 
更多关于Golang中exec.Command与Python交互的异常问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这并不奇怪,而是它的工作原理。
你的Go代码相当于在bash中运行 ./test.py "-p /some/path"。你需要在Go中分别传递它们。
显然,我在发布后立刻弄明白了。我仍然认为这个帖子值得留在这里,因为如果我都对此感到困惑,我相信其他人也会。
无论如何,这里的问题是,当使用终端向程序发送参数时,它会用空格分隔参数,因此以下语句:
./test.py -p /some/path 实际上被分割为:
./test.py
-p
/some/path
因此,在使用 exec.Command 时,我们需要采用相同的策略来发送它们。我之前的做法是像这样发送:
./test.py
-p /some/path
这意味着 -p 和 /somepath 被视为一个参数。要解决上述问题,只需将你在终端中用空格分隔的每个单词视为不同的参数,如下所示:
// 而不是:
cmd := exec.Command("./test.py", "-p /some/path")
// 应该:
cmd := exec.Command("./test.py", "-p", "/some/path")
这是一个典型的命令行参数传递问题。问题在于Go的exec.Command参数传递方式与直接在终端执行不同。
在你的Go代码中,"-p /some/path"被当作一个完整的字符串参数传递给Python,而不是两个独立的参数。正确的做法应该是将参数分开传递:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
// 将参数分开传递,而不是作为一个字符串
cmd := exec.Command("./test.py", "-p", "/some/path")
fmt.Println(cmd.Args)
output, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("Error: %s", err)
}
fmt.Printf("\nOutput received: \n %s", output)
}
或者,如果你确实需要将参数作为一个字符串传递,可以使用bash -c:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("bash", "-c", "./test.py -p /some/path")
fmt.Println(cmd.Args)
output, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("Error: %s", err)
}
fmt.Printf("\nOutput received: \n %s", output)
}
为了验证这个问题,你可以在Python脚本中添加调试信息:
#!/usr/bin/python3
import argparse
import sys
print(f"sys.argv: {sys.argv}")
print(f"Number of arguments: {len(sys.argv)}")
for i, arg in enumerate(sys.argv):
print(f"argv[{i}]: >>{arg}<<")
parser = argparse.ArgumentParser()
parser.add_argument('--path', '-p', dest='path')
args = parser.parse_args()
print(f"The path:>>{args.path}<<")
使用这个调试脚本,你会看到:
- 当使用
exec.Command("./test.py", "-p /some/path")时,sys.argv是['./test.py', '-p /some/path'] - 当使用
exec.Command("./test.py", "-p", "/some/path")时,sys.argv是['./test.py', '-p', '/some/path']
argparse库期望每个命令行参数都是独立的数组元素,所以当"-p /some/path"作为一个整体传递时,argparse会尝试解析这个字符串,导致异常行为。

