英文:
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命令的输出,否则我无法从stdout/stderr中捕获上传百分比。我只能捕获成功消息和总传输字节数。
不使用/usr/bin/script
的percentage.txt
输出示例:
Completed: 2389K bytes transferred in 1 seconds
(10275K bits/sec), in 3 files, 1 directory.
使用/usr/bin/script
的percentage.txt
输出示例。可以看到,我能够保留上传百分比:
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实例的命令行中运行时,没有任何问题,它按预期工作。
然而,当我尝试通过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)
}
}
英文:
I am using Go to automate IBM Aspera uploads. I would like to capture the stdout/stderr percentage progress while this happens.
I am running this on an ubuntu:latest
docker image
This is the entire command that I am trying to run in 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
This is how I am calling this command in my Go project
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")
I have found I have to use /usr/bin/script to capture the output of the ascp command, otherwise I am unable to capture the upload percentage from the stdout/stderr. I am only able to capture the success message and total bytes transferred.
Example percentage.txt
output WITHOUT using /usr/bin/script:
Completed: 2389K bytes transferred in 1 seconds
(10275K bits/sec), in 3 files, 1 directory.
Example percentage.txt
output WITH using /usr/bin/script. As you can see, I am able to preserve the upload percentage:
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.
When I take the raw command above, and run it directly on the cli of my docker instance, I have no issue and it works as expected.
However, when I attempt to run the command through the exec.Command()
function, I receive this output
/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
When I run println(cmd.String())
this is my output:
/usr/bin/script -e -c 'ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>' > percentage.txt 2>&1
And if I copy and paste the outputted command string from the cmd.string
into my terminal, instead of running it through exec.Command()
, it works and the percentage.txt
captures the upload status.
What am I doing wrong here? Why is go's exec.Command()
unable to run this, but my shell prompt is?
Full code:
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)
}
}
答案1
得分: 1
您的命令行输入存在错误的标记化。如果这是一个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有特殊含义的字符(如>
),并且需要小心正确引用所有内容。
英文:
You have mis-tokenized your command line. If this is the shell 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
Then the corresponding exec.Command
would almost be:
cmd := exec.Command("/usr/bin/script", "-e", "-c", "ascp -P <port> -m 3g -l 3g -i <secret> local_folder <user>@<host>:<remote-folder>")
That is, everything enclosed by single quotes on the command line is a single argument.
Using this version of the command, you will need to handle output redirection yourself (by reading the output from the command and writing it to a file).
If you really don't want to deal with reading the output from the command, you could explicitly call out to a 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")
...but this is typically less efficient and less robust, because now you need to be careful about characters (like >
) that have special meaning to the shell and you need to be careful about properly quoting everything.
答案2
得分: 0
直接使用ascp
会带来复杂性,并且您将尝试解析可能随时更改的日志。
该SDK提供了一个GRPC接口和Go存根。
该SDK提供了失败传输的自动恢复、进度监控、多会话等功能。
该SDK是免费的,可以从IBM开发者网站下载。
英文:
Using ascp
directly will bring complexity and you will try to parse logs that may anyway change any time.
Have look to the Aspera Transfer SDK.
This SDK provides a GRPC interface and Go stubs.
The SDK provides auto-resume of failed transfers, progress monitoring, multi-session, etc...
The SDK is free and can be downloaded from the IBM developer site.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论