在 Go 中执行 shell 命令

发布于 2024-11-10 10:23:40 字数 544 浏览 5 评论 0原文

我希望在 Go 中执行 shell 命令,并在程序中以字符串形式获取结果输出。我看到了 Rosetta Code 版本:

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

谜兔 2024-11-17 10:23:40

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

    // Print the output
    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))
}
对你再特殊 2024-11-17 10:23:40

提供的答案都不允许将 stdout 和 stderr 分开,所以我尝试另一个答案。

首先,如果您查看 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)
}

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)
}
笑,眼淚并存 2024-11-17 10:23:40

此答案并不代表 Go 标准库的当前状态。请查看 @Lourenco 的回答以获取最新方法!


您的示例实际上并未读取来自标准输出的数据。这对我有用。

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

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()
}
彡翼 2024-11-17 10:23:40
// 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
    }
}

你可以试试这个。

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

末骤雨初歇 2024-11-17 10:23:40
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))
}

使用 exec.Command 函数创建一个新的 cmd 进程,并使用 /C 标志和 dir 命令作为其参数
使用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.

っ〆星空下的拥抱 2024-11-17 10:23:40

这是一个简单的函数,它将运行您的命令并捕获错误、标准输出和标准错误以供您检查。您可以轻松地看到任何可能出现问题或向您报告的情况。

// 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 中使用过它

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

酒儿 2024-11-17 10:23:40

如果您希望异步运行长时间运行的脚本并显示执行进度,您可以使用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())
}

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())
}
断桥再见 2024-11-17 10:23:40

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


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

拍不死你 2024-11-17 10:23:40

我没有让 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()

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.

只涨不跌 2024-11-17 10:23:40

我最终这样做是为了获取退出错误代码、命令输出和错误:

parts := strings.Split("netstat -tupln", " ")
cmd := exec.Command(parts[0], parts[1:]...)
out, err := cmd.Output()
exitCode := cmd.ProcessState.ExitCode()

在上面答案(以及 stackoverflow 上的其他问题)的帮助下,希望这对某人有帮助。

I ended up doing this to get exit error code, command output and error:

parts := strings.Split("netstat -tupln", " ")
cmd := exec.Command(parts[0], parts[1:]...)
out, err := cmd.Output()
exitCode := cmd.ProcessState.ExitCode()

with the help from answers above (and other questions on stackoverflow), hope this helps someone.

○愚か者の日 2024-11-17 10:23:40

这是使用 cmdx 库的一个很好的替代方案,它使执行命令和捕获输出更简单、更

package main

import (
    "github.com/kgs19/cmdx"
    "log"
)

func main() {
    command := "date"
    args := []string{"+%H:%M"}
    out, err := cmdx.RunCommandReturnOutput(command, args...)
    if err != nil {
        log.Fatalf("Error executing 'date' command: %v", err)
    }
    println("cmd output: " + out)
}

  • 直观 code>cmdx.RunCommandReturnOutput(command, args...):此函数使用给定参数运行指定命令,并将输出作为字符串返回。

有关更多信息,请查看 GitHub 存储库

Here's a great alternative using the cmdx library that makes executing commands and capturing output simpler and more intuitive:

package main

import (
    "github.com/kgs19/cmdx"
    "log"
)

func main() {
    command := "date"
    args := []string{"+%H:%M"}
    out, err := cmdx.RunCommandReturnOutput(command, args...)
    if err != nil {
        log.Fatalf("Error executing 'date' command: %v", err)
    }
    println("cmd output: " + out)
}

Explanation

  • cmdx.RunCommandReturnOutput(command, args...): This function runs the specified command with the given arguments and returns the output as a string.

For more information, check out the GitHub repository.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文