在 Go 中,当 运行 exec.Command 和 /usr/bin/script 前一个命令时,会抛出一个错误: /usr/bin/script: invalid option -- 'P'

In Go, when running exec.Command with /usr/bin/script before a command, an error is thrown: /usr/bin/script: invalid option -- 'P'

我正在使用 Go 来自动执行 IBM Aspera 上传。我想在发生这种情况时捕获 stdout/stderr 百分比进度。

我运行在 ubuntu:latest docker 图像上 运行 宁此

这是我在 go 中尝试 运行 的完整命令。

/usr/bin/script -e -c 'ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>' > percentage.txt 2>&1

这就是我在 Go 项目中调用此命令的方式

cmd := exec.Command("/usr/bin/script", "-e", "-c", "'ascp", "-P <port>", "-m 3g", "-l 3g", "-i <secret>", "local_folder", "<user>@<host>:<remote-folder>'", "> percentage.txt", "2>&1")

我发现我使用/usr/bin/script来捕获ascp命令的输出,否则我无法从[=59]捕获上传百分比=].我只能捕获成功消息和传输的总字节数。

示例 percentage.txt 输出不使用 /usr/bin/script:

Completed: 2389K bytes transferred in 1 seconds
 (10275K bits/sec), in 3 files, 1 directory.

示例 percentage.txt 使用 /usr/bin/script 输出 WITH。如您所见,我能够保留上传百分比:

Script started, output log file is 'typescript'.
<sample_file>                                                                                       100% 2194KB 13.3Mb/s    00:01    
<sample_file>                                                                                       100%  192KB 13.3Mb/s    00:01    
<sample_file>                                                                                       100% 3329   13.3Mb/s    00:01    
Completed: 2389K bytes transferred in 1 seconds
 (12268K bits/sec), in 3 files, 1 directory.
Script done.

当我直接在我的 docker 实例的 cli 上使用上面的原始命令 运行 时,我没有遇到任何问题,它按预期工作。

但是,当我尝试通过 exec.Command() 函数 运行 命令时,我收到此输出

/usr/bin/script: invalid option -- 'P'
Try 'script --help' for more information.


exit status 1


panic: exit status 1

goroutine 1 [running]:
main.main()
        /project_dir/main.go:40 +0x3e7
exit status 2

当我 运行 println(cmd.String()) 这是我的输出:

/usr/bin/script -e -c 'ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>' > percentage.txt 2>&1

如果我将 cmd.string 输出的命令字符串复制并粘贴到我的终端,而不是 运行 通过 exec.Command() 将其粘贴,它就可以工作并且 percentage.txt 捕获上传状态。

我在这里做错了什么?为什么go的exec.Command()不能运行这个,而我的shell提示是?

完整代码:

func main() {
    f, err := os.OpenFile("percentage.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }
    defer f.Close()

    mwriter := io.MultiWriter(f, os.Stdout)

    err = os.Setenv("SHELL", "bash")
    if err != nil {
        return
    }
    cmd := exec.Command("/usr/bin/script", "-e", "-c", "'ascp", "-P <port>", "-m 3g", "-l 3g", "-i <secret>", "local_folder", "<user>@<host>:<remote-folder>'", "> percentage.txt", "2>&1")
    println(cmd.String())

    cmd.Stderr = mwriter
    cmd.Stdout = mwriter

    err = cmd.Run()
    if err != nil {
        println("\n\n" + err.Error() + "\n\n")
        panic(err)
    }
}

您有 mis-tokenized 命令行。如果这是 shell 命令:

/usr/bin/script -e -c 'ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>' > percentage.txt 2>&1

那么对应的exec.Command差不多就是:

cmd := exec.Command("/usr/bin/script", "-e", "-c", "ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>")

也就是说,命令行中用单引号括起来的所有内容都是一个参数。

使用此版本的命令,您需要自己处理输出重定向(通过读取命令的输出并将其写入文件)。


如果你真的不想处理读取命令的输出,你可以显式调用 shell:

cmd := exec.Command("/bin/sh", "-c", "/usr/bin/script -e -c 'ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>' > percentage.txt 2>&1")

...但这通常效率较低且鲁棒性较差,因为现在您需要注意对 shell 具有特殊含义的字符(如 >)并且您需要小心正确引用所有内容。

直接使用ascp会带来复杂性,您将尝试解析随时可能发生变化的日志。

查看Aspera Transfer SDK

此SDK提供GRPC接口和Go存根。

SDK提供auto-resume个失败转账,进度监控,multi-session等...

SDK 是免费的,可以从 IBM 开发者网站下载。