在Golang中真实的从Stdout获取数据

drnojrws  于 2023-04-03  发布在  Go
关注(0)|答案(1)|浏览(192)

在我的程序中,我希望能够真实的监控一些控制台命令的执行。
下面是两个在控制台中真实的显示数据的代码示例,但在网页上数据并不是实时显示的,而是仅在命令完成后才显示。
请告诉我如何在网页中获得真实的数据?

  • 在这两个例子中:控制台真实的显示数据。在网页上,数据不是实时显示,而是在命令完成后才显示。我需要在网页上实时显示数据。*

实施例1

package main

import (
    "bytes"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/exec"
    "io"
)

func main() {
    http.HandleFunc("/", mainLogic)
    http.ListenAndServe(":8080", nil)
}

func mainLogic(w http.ResponseWriter, r *http.Request) {
    cmd := exec.Command("ping", "8.8.8.8", "-c5")
    var stdBuffer bytes.Buffer
    mw := io.MultiWriter(os.Stdout, &stdBuffer)
    cmd.Stdout = mw
    cmd.Stderr = mw
    if err := cmd.Run(); err != nil {
        log.Panic(err)
    }
    fmt.Fprintf(w, stdBuffer.String())
}

实施例2

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "os/exec"
    "io"
    "sync"
)

func main() {
    http.HandleFunc("/", mainLogic)
    http.ListenAndServe(":8080", nil)
}

func mainLogic(w http.ResponseWriter, r *http.Request) {
    cmd := exec.Command("ping", "8.8.8.8", "-c5")
    var stdout []byte
    var errStdout, errStderr error
    stdoutIn, _ := cmd.StdoutPipe()
    stderrIn, _ := cmd.StderrPipe()
    err := cmd.Start()
    if err != nil {
        log.Fatalf("cmd.Start() failed with '%s'\n", err)
    }
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
        wg.Done()
    }()
    _, errStderr = copyAndCapture(os.Stderr, stderrIn)
    wg.Wait()
    err = cmd.Wait()
    if err != nil {
        log.Fatalf("cmd.Run() failed with %s\n", err)
    }
    if errStdout != nil || errStderr != nil {
        log.Fatal("failed to capture stdout or stderr\n")
    }
    fmt.Fprintf(w, string(stdout))
}

func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
    var out []byte
    buf := make([]byte, 1024, 1024)
    for {
        n, err := r.Read(buf[:])
        if n > 0 {
            d := buf[:n]
            out = append(out, d...)
            _, err := w.Write(d)
            if err != nil {
                return out, err
            }
        }
        if err != nil {
            if err == io.EOF {
                err = nil
            }
            return out, err
        }
    }
}
l7wslrjt

l7wslrjt1#

出于性能原因,HTTP响应默认情况下是缓冲的,因此您需要显式调用Flush以立即发送数据。
如果你想从一个分配给exec.Cmd输出的writer中完成这个任务,你可以将http.ResponseWriter Package 在一个类型中,这个类型将在每个Write上为你调用Flush

type flushWriter interface {
    io.Writer
    http.Flusher
}

type flusher struct {
    w flushWriter
}

func (f *flusher) Write(d []byte) (int, error) {
    n, err := f.w.Write(d)
    if err != nil {
        return n, err
    }
    f.w.Flush()
    return n, nil
}

...

if w, ok := w.(flushWriter); ok {
    cmd.Stdout = &flusher{w}
    cmd.Stderr = &flusher{w}
}

如果你想要一份数据的拷贝,你可以使用io.MultiWriter在输出被写入的时候捕获它。这里我们还介绍了http.ResponseWriter不是http.Flusher的情况,但是只有当你已经通过其他中间件 Package 时才可能发生这种情况。

var mw io.Writer
var buff bytes.Buffer
if w, ok := w.(flushWriter); ok {
    mw = io.MultiWriter(&buff, &flusher{w})
} else {
    mw = io.MultiWriter(&buff, w)
}
cmd.Stdout = mw
cmd.Stderr = mw

相关问题