Properly passing data on stdin to a command and receiving data from stdout of that command in golang

后端 未结 2 1413
遥遥无期
遥遥无期 2021-02-05 20:49

I have the following program:

package main

import \"bytes\"
import \"io\"
import \"log\"
import \"os\"
import \"os/exec\"
import \"time\"

func main() {
    run         


        
相关标签:
2条回答
  • 2021-02-05 20:59

    Go statements

    A "go" statement starts the execution of a function or method call as an independent concurrent thread of control, or goroutine, within the same address space.

    GoStmt = "go" Expression .

    The expression must be a call. The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.

    Convert the gratuitous goroutines to function calls.

    package main
    
    import (
        "bytes"
        "io"
        "log"
        "os"
        "os/exec"
    )
    
    func main() {
        runCatFromStdinWorks(populateStdin("aaa\n"))
        runCatFromStdinWorks(populateStdin("bbb\n"))
    }
    
    func populateStdin(str string) func(io.WriteCloser) {
        return func(stdin io.WriteCloser) {
            defer stdin.Close()
            io.Copy(stdin, bytes.NewBufferString(str))
        }
    }
    
    func runCatFromStdinWorks(populate_stdin_func func(io.WriteCloser)) {
        cmd := exec.Command("cat")
        stdin, err := cmd.StdinPipe()
        if err != nil {
            log.Panic(err)
        }
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            log.Panic(err)
        }
        err = cmd.Start()
        if err != nil {
            log.Panic(err)
        }
        populate_stdin_func(stdin)
        io.Copy(os.Stdout, stdout)
        err = cmd.Wait()
        if err != nil {
            log.Panic(err)
        }
    }
    
    0 讨论(0)
  • 2021-02-05 21:01

    Here is a version of your first code which works. Note the addition of the sync.WaitGroup to make sure you finish with the sending and receiving go routines before closing the command.

    package main
    
    import (
        "bytes"
        "io"
        "log"
        "os"
        "os/exec"
        "sync"
        "time"
    )
    
    func main() {
        runCatFromStdinWorks(populateStdin("aaa\n"))
        runCatFromStdinWorks(populateStdin("bbb\n"))
    }
    
    func populateStdin(str string) func(io.WriteCloser) {
        return func(stdin io.WriteCloser) {
            defer stdin.Close()
            io.Copy(stdin, bytes.NewBufferString(str))
        }
    }
    
    func runCatFromStdinWorks(populate_stdin_func func(io.WriteCloser)) {
        cmd := exec.Command("cat")
        stdin, err := cmd.StdinPipe()
        if err != nil {
            log.Panic(err)
        }
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            log.Panic(err)
        }
        err = cmd.Start()
        if err != nil {
            log.Panic(err)
        }
        var wg sync.WaitGroup
        wg.Add(2)
        go func() {
            defer wg.Done()
            populate_stdin_func(stdin)
        }()
        go func() {
            defer wg.Done()
            time.Sleep(5 * time.Second)
            io.Copy(os.Stdout, stdout)
        }()
        wg.Wait()
        err = cmd.Wait()
        if err != nil {
            log.Panic(err)
        }
    }
    

    (This is just another way of saying what @peterSO said though ;-)

    0 讨论(0)
提交回复
热议问题