在Go语言中执行shell命令

cig3rfwq  于 2022-12-07  发布在  Go
关注(0)|答案(8)|浏览(138)

我想在Go语言中执行一个shell命令,并在我的程序中得到一个字符串形式的输出。

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()

但这并没有以我可以编程访问的方式捕获实际的标准输出或错误-这些仍然打印输出到常规的stdout / stderr。我看到使用Pipe作为输出或错误在其他地方可能会有帮助,但没有示例说明如何这样做。有什么想法吗?

h5qlskok

h5qlskok1#

包“exec”有点像changed。下面的代码对我很有效。

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))
}

我希望这对你有帮助!

rslzwgfq

rslzwgfq2#

提供的答案都不允许区分stdoutstderr,因此我尝试另一个答案。
首先,如果你查看os/exec包中exec.Cmd类型的文档,你会得到所有你需要的信息。https://golang.org/pkg/os/exec/#Cmd
特别是成员StdinStdoutStderr,其中任何io.Reader都可以用来提供新创建的进程的stdin,任何io.Writer都可以用来使用命令的stdoutstderr
下面程序中的函数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)
}
bihw5rsg

bihw5rsg3#

这个答案并不代表Go语言标准库的现状。请看@Lourenco的答案,了解最新的方法!

您的示例实际上并没有从stdout读取数据。

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()
}
d5vmydt9

d5vmydt94#

// 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
    }
}

你可以试试这个。

nnvyjq4y

nnvyjq4y5#

下面是一个简单的函数,它将运行您的命令并捕获error、stdout和stderr以供您检查。您可以很容易地看到任何可能出错或报告给您的内容。

// 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
}

您可以像这样使用它(转换媒体文件):

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中使用了此功能

gmxoilav

gmxoilav6#

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
}

围棋测试

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)
    }
}
r7xajy2e

r7xajy2e7#

我没有让Rosetta的例子在我的Windows Go中工作。最后,我设法用这个命令在Windows的记事本中启动outfile,越过了子进程的旧格式。在一本手册中提到的wait常量参数不存在,所以我只是省略了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等待,等待已改变的方法,我成功地做到:

defer p.Wait(0)

后来我决定

defer p.Release()

得双曲余切值.

kokeuurv

kokeuurv8#

如果您想要与执行进度异步运行长时间运行的脚本,您可以使用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()  // Starts command asynchronously

if err != nil {
    fmt.Printf(err.Error())
}

相关问题