Go - 使用 goroutines 执行 Bash 命令 n 次并存储并打印其结果

Go - Execute a Bash Command n Times using goroutines and Store & Print its result

总的来说,我对 Golang 很陌生,我正在尝试执行 bash command with its arguments n times,然后将 Output 存储在一个变量中,然后 Print它。

我只能做一次,或者像下面这样使用 loops

package main

import (
    "fmt"
    "os/exec"
    "os"
    "sync"
)


func main() {

    //Default Output
    var (
        cmdOut []byte
        err    error
    )

    //Bash Command
    cmd  := "./myCmd"
   //Arguments to get passed to the command
   args := []string{"arg1", "arg2", "arg3"}

    //Execute the Command
    if cmdOut, err = exec.Command(cmd, args...).Output(); err != nil {
        fmt.Fprintln(os.Stderr, "There was an error running "+cmd+" "+args[0]+args[1]+args[2], err)
        os.Exit(1)
    }
    //Store it
    sha := string(cmdOut)
    //Print it
    fmt.Println(sha)
}

这很好用,我可以轻松阅读 output

现在,我想使用 goroutines.

重复同样的操作 n

我尝试采用与回答 How would you define a pool of goroutines to be executed at once in Golang? 的人完全相同的方法,但我无法做到这一点。

这就是我到目前为止所尝试的:

package main

import (
    "fmt"
    "os/exec"
    "sync"
)


func main() {

    //Bash Command
    cmd  := "./myCmd"
    //Arguments to get passed to the command
     args := []string{"arg1", "arg2", "arg3"}

    //Common Channel for the goroutines
    tasks := make(chan *exec.Cmd, 64)

    //Spawning 4 goroutines
    var wg sync.WaitGroup
    for i := 0; i < 4; i++ {
        wg.Add(1)
        go func() {
            for cmd := range tasks {
                cmd.Run()
            }
            wg.Done()
        }()
    }

    //Generate Tasks
    for i := 0; i < 10; i++ {
        tasks <- exec.Command(cmd, args...)
        //Here I should somehow print the result of the latter command
    }
    close(tasks)

    // wait for the workers to finish
    wg.Wait()

    fmt.Println("Done")

}

但是,我真的不知道如何存储已执行命令的 i-result 并打印它。

我该如何实现?

提前致谢,如需对问题进行任何澄清,请发表评论。

因此以下内容可以解决您的问题

  • 您可以调用Cmd.Output() 来获取命令的输出。 否则你可以将 Cmd.StdOutPipe 管道连接到 byte.Buffer 示例并从中阅读。

  • 你的 goroutine 逻辑是错误的。它只会执行 4 次,因为等待组将是 Done() 并且 main 将退出。您关闭 main 的通道以向工作人员发出退出 for range 循环的信号是正确的。 wg.Done 应该在那之后调用,所以我推迟了它。

  • 当您使用 go 命令执行匿名函数时,尽量不要从父范围捕获任何内容。它可能导致灾难。而不是找出你想要的参数并将它们传递给函数。

`

package main 

import (
    "fmt"
    "os/exec"
    "sync"
)
func main() {
    cmd := "./foo.sh"
    //Arguments to get passed to the command
    args := []string{"bar", "baz"}

    //Common Channel for the goroutines
    tasks := make(chan *exec.Cmd, 64)

    //Spawning 4 goroutines
    var wg sync.WaitGroup
    for i := 0; i < 4; i++ {
            wg.Add(1)
            go func(num int, w *sync.WaitGroup) {
                    defer w.Done()
                    var (
                            out []byte
                            err error
                    )
                    for cmd := range tasks { // this will exit the loop when the channel closes
                            out, err = cmd.Output()
                            if err != nil {
                                    fmt.Printf("can't get stdout:", err)
                            }
                            fmt.Printf("goroutine %d command output:%s", num, string(out))
                    }
            }(i, &wg)
    }
    //Generate Tasks
    for i := 0; i < 10; i++ {
            tasks <- exec.Command(cmd, args...)
    }
    close(tasks)

    // wait for the workers to finish
    wg.Wait()

    fmt.Println("Done")

}

`