【问题标题】:Go - Execute a Bash Command n Times using goroutines and Store & Print its resultGo - 使用 goroutine 执行一个 Bash 命令 n 次并存储并打印其结果
【发布时间】:2016-10-25 19:02:00
【问题描述】:

总的来说,我对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 并打印它。

我怎样才能做到这一点?

在此先感谢,如果您对问题有任何澄清,请发表评论。

【问题讨论】:

    标签: bash go goroutine


    【解决方案1】:

    所以以下解决了您的问题

    • 您可以调用 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")
    
    }
    

    `

    【讨论】:

    • 完全是我想要的,谢谢。另一个小问题。您认为这是使用Go 进行“并行”操作的最具成本效益的方式吗?我必须为1.000.000 不同的a[0] 参数这样做,并将结果存储在filedb(例如MongoDB)中。
    • 这真的取决于。运行 goroutines 是一种将工作分配给数以万计工人的绝妙方式。我担心在你的问题中你可能会用你的数据库驱动程序碰壁。许多 golang 数据库连接器可以很好地与很多工作人员一起扩展(我不了解 MongoDB,但在我使用过的 cassandra 中,请确保让它们共享数据库会话。)。另一端正在从您的脚本运行 100 万个进程。再次......命令的中位运行时间是多少?您可以通过在一个命令调用中合并数千个参数来批量操作吗?
    • 它需要像42 seconds 来执行它并打印它100.000 次。这是一个相当慢的Ocaml encryption script。我每次都必须传递不同的6 Hex Chars 并存储结果。好吧,有了NodeJS,我真的很难获得这样的表现。现在,正如您所说,我必须测试将结果保存在MongoDB 上是否是个好主意。不幸的是,不,我每次通过它的次数不能超过a[0]。它接受-sHex KeyA 24 Hex Chars PlainText。所以我必须按照000000 - FFFFFF 范围内的每个不同键执行它。
    • 嗯,如果没有更多细节,我真的无能为力。但请记住,磁盘 i/o 很慢,也可以打印到标准输出。我会调查写入 byte.Buffers(使用 StdoutPipe 甚至附加 Output()),然后使用计数器将整个结果缓冲区刷新到磁盘或数据库。您不需要每次执行脚本都执行一次存储操作。
    • 我会深入研究,非常感谢,非常感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2021-07-13
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多