英文:
Exec a shell command in Go
问题
我想在Go中执行一个shell命令,并将结果输出作为字符串返回到我的程序中。我看到了Rosetta Code的版本:
package main
import "fmt"
import "os/exec"
func main() {
cmd := exec.Command("/bin/ls", "/bin/ls")
out, err := cmd.Output()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(out))
}
但是这种方法无法捕获实际的标准输出或错误,它们仍然会打印到常规的stdout / stderr。我看到在其他地方使用Pipe作为输出或错误可能会有帮助,但没有示例说明如何使用。有什么想法吗?
英文:
I'm looking to execute a shell command in Go and get the resulting output as a string in my program. I saw the Rosetta Code version:
package main
import "fmt"
import "exec"
func main() {
cmd, err := exec.Run("/bin/ls", []string{"/bin/ls"}, []string{}, "", exec.DevNull, exec.PassThrough, exec.PassThrough)
if (err != nil) {
fmt.Println(err)
return
}
cmd.Close()
But this doesn't capture the actual standard out or err in a way that I can programatically access - those still print out to the regular stdout / stderr. I saw that using Pipe as the out or err could help elsewhere, but no example of how to do so. Any ideas?
答案1
得分: 242
包“exec”稍微有些变化。以下代码对我有效。
package main
import (
"fmt"
"os/exec"
)
func main() {
app := "echo"
arg0 := "-e"
arg1 := "Hello world"
arg2 := "\n\tfrom"
arg3 := "golang"
cmd := exec.Command(app, arg0, arg1, arg2, arg3)
stdout, err := cmd.Output()
if err != nil {
fmt.Println(err.Error())
return
}
// 打印输出
fmt.Println(string(stdout))
}
英文:
The package "exec" was changed a little bit. The following code worked for me.
package main
import (
"fmt"
"os/exec"
)
func main() {
app := "echo"
arg0 := "-e"
arg1 := "Hello world"
arg2 := "\n\tfrom"
arg3 := "golang"
cmd := exec.Command(app, arg0, arg1, arg2, arg3)
stdout, err := cmd.Output()
if err != nil {
fmt.Println(err.Error())
return
}
// Print the output
fmt.Println(string(stdout))
}
答案2
得分: 79
提供的答案都不能将stdout
和stderr
分开,所以我尝试了另一个答案。
首先,你需要获取所有你需要的信息,如果你查看os/exec
包中exec.Cmd
类型的文档。请看这里:https://golang.org/pkg/os/exec/#Cmd
特别是Stdin
、Stdout
和Stderr
成员,你可以使用任何io.Reader
来提供新创建进程的stdin
,并且可以使用任何io.Writer
来消费命令的stdout
和stderr
。
下面的程序中的Shellout
函数将运行你的命令,并将输出和错误输出分别作为字符串返回。
由于参数值作为shell命令执行,所以在构造参数值时要对所有外部输入进行清理。
在生产环境中可能不要以这种形式使用。
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
)
const ShellToUse = "bash"
func Shellout(command string) (string, string, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(ShellToUse, "-c", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
return stdout.String(), stderr.String(), err
}
func main() {
out, errout, err := Shellout("ls -ltr")
if err != nil {
log.Printf("error: %v\n", err)
}
fmt.Println("--- stdout ---")
fmt.Println(out)
fmt.Println("--- stderr ---")
fmt.Println(errout)
}
英文:
None of the provided answers allow to separate stdout
and stderr
so I try another answer.
First you get all the info you need, if you look at the documentation of the exec.Cmd
type in the os/exec
package. Look here: https://golang.org/pkg/os/exec/#Cmd
Especially the members Stdin
and Stdout
,Stderr
where any io.Reader
can be used to feed stdin
of your newly created process and any io.Writer
can be used to consume stdout
and stderr
of your command.
The function Shellout
in the following programm will run your command and hand you its output and error output separatly as strings.
As the parameter value is executed as a shell command, sanitize
all external inputs used in the construction of the parameter
value.
Probably don't use it in this form in production.
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
)
const ShellToUse = "bash"
func Shellout(command string) (string, string, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(ShellToUse, "-c", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
return stdout.String(), stderr.String(), err
}
func main() {
out, errout, err := Shellout("ls -ltr")
if err != nil {
log.Printf("error: %v\n", err)
}
fmt.Println("--- stdout ---")
fmt.Println(out)
fmt.Println("--- stderr ---")
fmt.Println(errout)
}
答案3
得分: 12
这个答案并不代表Go标准库的当前状态。请查看@Lourenco的答案以获取最新的方法!
你的示例实际上没有从stdout中读取数据。以下是适用于我的代码:
package main
import (
"fmt"
"os/exec"
"os"
"bytes"
"io"
)
func main() {
app := "/bin/ls"
cmd := exec.Command(app, "-l")
var b bytes.Buffer
cmd.Stdout = &b
err := cmd.Run()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return
}
fmt.Println(b.String())
}
英文:
This answer does not represent the current state of the Go standard library. Please take a look at @Lourenco's answer for an up-to-date method!
Your example does not actually read the data from stdout. This works for me.
package main
import (
"fmt"
"exec"
"os"
"bytes"
"io"
)
func main() {
app := "/bin/ls"
cmd, err := exec.Run(app, []string{app, "-l"}, nil, "", exec.DevNull, exec.Pipe, exec.Pipe)
if (err != nil) {
fmt.Fprintln(os.Stderr, err.String())
return
}
var b bytes.Buffer
io.Copy(&b, cmd.Stdout)
fmt.Println(b.String())
cmd.Close()
}
答案4
得分: 7
// 包装exec函数,提供使用bash shell的选项
func Cmd(cmd string, shell bool) []byte {
if shell {
out, err := exec.Command("bash", "-c", cmd).Output()
if err != nil {
panic("发生了一些错误")
}
return out
} else {
out, err := exec.Command(cmd).Output()
if err != nil {
panic("发生了一些错误")
}
return out
}
}
你可以尝试这个。
英文:
// Wrap exec, with option to use bash shell
func Cmd(cmd string, shell bool) []byte {
if shell {
out, err := exec.Command("bash", "-c", cmd).Output()
if err != nil {
panic("some error found")
}
return out
} else {
out, err := exec.Command(cmd).Output()
if err != nil {
panic("some error found")
}
return out
}
}
you may try this .
答案5
得分: 3
这是一个简单的函数,它会运行你的命令并捕获错误、标准输出和标准错误供你检查。你可以轻松地查看任何可能出错或被报告给你的内容。
// RunCMD是一个简单的终端命令包装器
func RunCMD(path string, args []string, debug bool) (out string, err error) {
cmd := exec.Command(path, args...)
var b []byte
b, err = cmd.CombinedOutput()
out = string(b)
if debug {
fmt.Println(strings.Join(cmd.Args[:], " "))
if err != nil {
fmt.Println("RunCMD ERROR")
fmt.Println(out)
}
}
return
}
你可以像这样使用它(转换媒体文件):
args := []string{"-y", "-i", "movie.mp4", "movie_audio.mp3", "INVALID-ARG!"}
output, err := RunCMD("ffmpeg", args, true)
if err != nil {
fmt.Println("Error:", output)
} else {
fmt.Println("Result:", output)
}
我在Go 1.2-1.7中使用过这个函数。
英文:
Here is a simple function that will run your command and capture the error, stdout, and stderr for you to inspect. You can easily see anything that might go wrong or be reported back to you.
// RunCMD is a simple wrapper around terminal commands
func RunCMD(path string, args []string, debug bool) (out string, err error) {
cmd := exec.Command(path, args...)
var b []byte
b, err = cmd.CombinedOutput()
out = string(b)
if debug {
fmt.Println(strings.Join(cmd.Args[:], " "))
if err != nil {
fmt.Println("RunCMD ERROR")
fmt.Println(out)
}
}
return
}
You can use it like this (Converting a media file):
args := []string{"-y", "-i", "movie.mp4", "movie_audio.mp3", "INVALID-ARG!"}
output, err := RunCMD("ffmpeg", args, true)
if err != nil {
fmt.Println("Error:", output)
} else {
fmt.Println("Result:", output)
}
I've used this with Go 1.2-1.7
答案6
得分: 1
import (
"github.com/go-cmd/cmd"
)
const DefaultTimeoutTime = "1m"
func RunCMD(name string, args ...string) (err error, stdout, stderr []string) {
c := cmd.NewCmd(name, args...)
s := <-c.Start()
stdout = s.Stdout
stderr = s.Stderr
return
}
go test
import (
"fmt"
"gotest.tools/assert"
"testing"
)
func TestRunCMD(t *testing.T) {
err, stdout, stderr := RunCMD("kubectl", "get", "pod", "--context", "cluster")
assert.Equal(t, nil, err)
for _, out := range stdout {
fmt.Println(out)
}
for _, err := range stderr {
fmt.Println(err)
}
}
英文:
import (
"github.com/go-cmd/cmd"
)
const DefaultTimeoutTime = "1m"
func RunCMD(name string, args ...string) (err error, stdout, stderr []string) {
c := cmd.NewCmd(name, args...)
s := <-c.Start()
stdout = s.Stdout
stderr = s.Stderr
return
}
go test
import (
"fmt"
"gotest.tools/assert"
"testing"
)
func TestRunCMD(t *testing.T) {
err, stdout, stderr := RunCMD("kubectl", "get", "pod", "--context", "cluster")
assert.Equal(t, nil, err)
for _, out := range stdout {
fmt.Println(out)
}
for _, err := range stderr {
fmt.Println(err)
}
}
答案7
得分: 1
如果您想以异步方式运行长时间运行的脚本并显示执行进度,您可以使用io.MultiWriter
捕获命令输出并将其转发到stdout/stderr
:
import (
"fmt"
"io"
"os"
"os/exec"
)
var stdoutBuf, stderrBuf bytes.Buffer
cmd := exec.Command("/some-command")
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start() // 异步启动命令
if err != nil {
fmt.Printf(err.Error())
}
英文:
If you want run long-running script asynchronously with execution progress, you may capture command output using io.MultiWriter
and forward it to stdout/stderr
:
import (
"fmt"
"io"
"os"
"os/exec"
)
var stdoutBuf, stderrBuf bytes.Buffer
cmd := exec.Command("/some-command")
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start() // Starts command asynchronously
if err != nil {
fmt.Printf(err.Error())
}
答案8
得分: 1
使用exec.Command函数创建一个带有/C标志和dir命令作为参数的新cmd进程
使用output()方法捕获输出并打印出来。
英文:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("cmd", "/C", "dir")
output, err := cmd.Output()
if err != nil {
fmt.Println("Error executing command:", err)
return
}
fmt.Println(string(output))
}
Use the exec.Command function to create a new cmd process with the /C flag and the dir command as its argument
Capture output with output() method and print it.
答案9
得分: 0
我在我的Windows Go中无法使Rosetta示例工作。最后,我成功地通过以下命令绕过了旧格式的Subprocess,以在Windows中启动notepad的outfile。在一个手册中提到的等待常量参数不存在,所以我只是省略了Wait,因为用户会自己关闭程序或保持打开以重用。
p, err := os.StartProcess(`c:\windows\system32\notepad.EXE`,
[]string{`c:\windows\system32\notepad.EXE`, outfile},
&os.ProcAttr{Env: nil, Dir: "", Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}})
你可以将os.Stdout更改为os.Pipe,就像之前的回答一样。
编辑:我最终从godoc os Wait中找到了解决方法,Wait已经变成了方法,我成功地执行了以下操作:
defer p.Wait(0)
然后我最终决定使用
defer p.Release()
代替。
英文:
I did not get the Rosetta example to work in my Windows Go. Finally I managed to go past the old format of the Subprocess with this command to start outfile in notepad in windows. The wait constant parameter mentioned in one manual did not exist so I just left out Wait as the user will close the program by themself or leave it open to reuse.
p, err := os.StartProcess(`c:\windows\system32\notepad.EXE`,
[]string{`c:\windows\system32\notepad.EXE`, outfile},
&os.ProcAttr{Env: nil, Dir: "", Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}})
You would change the os.Stdout.. to os.Pipe as previous answer
EDIT: I got it finally from godoc os Wait, that Wait has changed to method of and I succeeded to do:
defer p.Wait(0)
Then I decided finally to put
defer p.Release()
instead.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论