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

huangapple go评论89阅读模式
英文:

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/scriptpercentage.txt输出示例:

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

使用/usr/bin/scriptpercentage.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 &#39;ascp -P &lt;port&gt; -m 3g -l 3g -i &lt;secret&gt; local_folder &lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&#39; &gt; percentage.txt 2&gt;&amp;1

This is how I am calling this command in my Go project

cmd := exec.Command(&quot;/usr/bin/script&quot;, &quot;-e&quot;, &quot;-c&quot;, &quot;&#39;ascp&quot;, &quot;-P &lt;port&gt;&quot;, &quot;-m 3g&quot;, &quot;-l 3g&quot;, &quot;-i &lt;secret&gt;&quot;, &quot;local_folder&quot;, &quot;&lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&#39;&quot;, &quot;&gt; percentage.txt&quot;, &quot;2&gt;&amp;1&quot;)

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 &#39;typescript&#39;.
&lt;sample_file&gt;				                                                                        100% 2194KB 13.3Mb/s    00:01    
&lt;sample_file&gt;				                                                                        100%  192KB 13.3Mb/s    00:01    
&lt;sample_file&gt;				                                                                        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 -- &#39;P&#39;
Try &#39;script --help&#39; 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 &#39;ascp -P &lt;port&gt; -m 3g -l 3g -i &lt;secret&gt; local_folder &lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&#39; &gt; percentage.txt 2&gt;&amp;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(&quot;percentage.txt&quot;, 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(&quot;SHELL&quot;, &quot;bash&quot;)
	if err != nil {
		return
	}
	cmd := exec.Command(&quot;/usr/bin/script&quot;, &quot;-e&quot;, &quot;-c&quot;, &quot;&#39;ascp&quot;, &quot;-P &lt;port&gt;&quot;, &quot;-m 3g&quot;, &quot;-l 3g&quot;, &quot;-i &lt;secret&gt;&quot;, &quot;local_folder&quot;, &quot;&lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&#39;&quot;, &quot;&gt; percentage.txt&quot;, &quot;2&gt;&amp;1&quot;)
	println(cmd.String())

	cmd.Stderr = mwriter
	cmd.Stdout = mwriter

	err = cmd.Run()
	if err != nil {
		println(&quot;\n\n&quot; + err.Error() + &quot;\n\n&quot;)
		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 &#39;ascp -P &lt;port&gt; -m 3g -l 3g -i &lt;secret&gt; local_folder &lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&#39; &gt; percentage.txt 2&gt;&amp;1

Then the corresponding exec.Command would almost be:

cmd := exec.Command(&quot;/usr/bin/script&quot;, &quot;-e&quot;, &quot;-c&quot;, &quot;ascp -P &lt;port&gt; -m 3g -l 3g -i &lt;secret&gt; local_folder &lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&quot;)

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(&quot;/bin/sh&quot;, &quot;-c&quot;, &quot;/usr/bin/script -e -c &#39;ascp -P &lt;port&gt; -m 3g -l 3g -i &lt;secret&gt; local_folder &lt;user&gt;@&lt;host&gt;:&lt;remote-folder&gt;&#39; &gt; percentage.txt 2&gt;&amp;1&quot;)

...but this is typically less efficient and less robust, because now you need to be careful about characters (like &gt;) that have special meaning to the shell and you need to be careful about properly quoting everything.

答案2

得分: 0

直接使用ascp会带来复杂性,并且您将尝试解析可能随时更改的日志。

请查看Aspera Transfer SDK

该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.

huangapple
  • 本文由 发表于 2022年5月6日 08:23:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/72134887.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定