Golang中如何捕获持续运行命令的标准输出

Golang中如何捕获持续运行命令的标准输出 我知道我们可以像这样收集命令的标准输出:

package main

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

func main() {
    out, err := exec.Command("date").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("The date is %s\n", string(out))
}

或者像这样:

package main

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"strings"
)

func main() {
	cmd := exec.Command("tr", "a-z", "A-Z")
	cmd.Stdin = strings.NewReader("some input")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("in all caps: %q\n", out.String())
}

但这通常适用于那些执行完成、返回输出然后退出的命令。

在我的情况下,我有一个来自正在运行的服务器命令,它会持续向标准输出提供日志,类似于:

waLog.Stdout("Client", "ERROR", true)

部分输出如下:

enter image description here

我如何在我的代码中捕获这个输出,类似于上面的 cmd.Stdout


更多关于Golang中如何捕获持续运行命令的标准输出的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

您可以指定一个不同的“io.writer”来处理您的输出。

更多关于Golang中如何捕获持续运行命令的标准输出的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中捕获持续运行命令的标准输出,需要使用管道实时读取。以下是两种常用方法:

方法1:使用cmd.StdoutPipe()实时读取

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("your-server-command", "arg1", "arg2")
    
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    
    // 实时读取输出
    reader := bufio.NewReader(stdout)
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }
            log.Printf("读取错误: %v", err)
            break
        }
        fmt.Printf("输出: %s", line)
        // 这里可以处理每一行输出
    }
    
    cmd.Wait()
}

方法2:使用io.MultiWriter同时输出到控制台和变量

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "io"
    "log"
    "os/exec"
    "sync"
)

func main() {
    cmd := exec.Command("tail", "-f", "/var/log/syslog")
    
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    
    var buf bytes.Buffer
    writer := io.MultiWriter(&buf, os.Stdout)
    
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    
    var wg sync.WaitGroup
    wg.Add(1)
    
    go func() {
        defer wg.Done()
        scanner := bufio.NewScanner(stdout)
        for scanner.Scan() {
            line := scanner.Text()
            fmt.Fprintln(writer, line)
            // 实时处理每一行
            processLine(line)
        }
    }()
    
    wg.Wait()
    cmd.Wait()
}

func processLine(line string) {
    // 这里处理每一行输出
    fmt.Printf("处理: %s\n", line)
}

方法3:同时捕获stdout和stderr

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os/exec"
    "sync"
)

func main() {
    cmd := exec.Command("your-command")
    
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    
    stderr, err := cmd.StderrPipe()
    if err != nil {
        log.Fatal(err)
    }
    
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    
    var wg sync.WaitGroup
    wg.Add(2)
    
    // 捕获stdout
    go func() {
        defer wg.Done()
        reader := bufio.NewReader(stdout)
        for {
            line, err := reader.ReadString('\n')
            if err != nil {
                if err == io.EOF {
                    break
                }
                log.Printf("stdout读取错误: %v", err)
                break
            }
            fmt.Printf("STDOUT: %s", line)
        }
    }()
    
    // 捕获stderr
    go func() {
        defer wg.Done()
        reader := bufio.NewReader(stderr)
        for {
            line, err := reader.ReadString('\n')
            if err != nil {
                if err == io.EOF {
                    break
                }
                log.Printf("stderr读取错误: %v", err)
                break
            }
            fmt.Printf("STDERR: %s", line)
        }
    }()
    
    wg.Wait()
    cmd.Wait()
}

方法4:使用带缓冲的通道处理输出

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("ping", "google.com")
    
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    
    outputChan := make(chan string, 100)
    
    // 启动goroutine读取输出
    go func() {
        reader := bufio.NewReader(stdout)
        for {
            line, err := reader.ReadString('\n')
            if err != nil {
                if err == io.EOF {
                    close(outputChan)
                    break
                }
                log.Printf("读取错误: %v", err)
                close(outputChan)
                break
            }
            outputChan <- line
        }
    }()
    
    // 主goroutine处理输出
    for line := range outputChan {
        fmt.Printf("收到输出: %s", line)
        // 这里可以添加业务逻辑
    }
    
    cmd.Wait()
}

关键点:

  1. 使用cmd.StdoutPipe()获取管道而不是直接赋值
  2. cmd.Start()之后立即开始读取,而不是cmd.Run()
  3. 使用goroutine持续读取,避免阻塞
  4. 对于长时间运行的服务,需要处理读取循环和错误情况
回到顶部