如何在golang中获取shell命令的实时输出?

sea*_*est 12 shell go

我试图在golang中使用os/exec调用shell命令,该命令将花费一些时间,因此我想检索reatime输出并打印处理后的输出(进度比率数字).

package main

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

func main() {
    cmdName := "ffmpeg -i t.webm  -acodec aac -vcodec libx264  cmd1.mp4"
    cmdArgs := strings.Fields(cmdName)

    cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
    stdout, _ := cmd.StdoutPipe()
    cmd.Start()
    go print(stdout)
    cmd.Wait()
}

// to print the processed information when stdout gets a new line
func print(stdout io.ReadCloser) {
    r := bufio.NewReader(stdout)
    line, _, err := r.ReadLine()
    fmt.Println("line: %s err %s", line, err)
}
Run Code Online (Sandbox Code Playgroud)

我希望有一个功能,当命令打印时可以更新屏幕,

ffmpeg命令输出如下:

frame=  101 fps=0.0 q=28.0 size=      91kB time=00:00:04.13 bitrate= 181.2kbits/
frame=  169 fps=168 q=28.0 size=     227kB time=00:00:06.82 bitrate= 272.6kbits/
frame=  231 fps=153 q=28.0 size=     348kB time=00:00:09.31 bitrate= 306.3kbits/
frame=  282 fps=140 q=28.0 size=     499kB time=00:00:11.33 bitrate= 360.8kbits/
Run Code Online (Sandbox Code Playgroud)

事实上,上面的4行是ffmpeg命令输出的最后一行,它不断变化,我想打印出来的变化,就像

18%
44%
69%
100%
Run Code Online (Sandbox Code Playgroud)

我怎么能实现这个目标?

Bro*_*Lin 11

看起来ffmpeg将所有诊断消息("控制台输出")发送到stderr而不是stdout.下面的代码适合我.

package main

import (
    "bufio"
    "fmt"
    "os/exec"
    "strings"
)

func main() {
    args := "-i test.mp4 -acodec copy -vcodec copy -f flv rtmp://aaa/bbb"
    cmd := exec.Command("ffmpeg", strings.Split(args, " ")...)

    stderr, _ := cmd.StderrPipe()
    cmd.Start()

    scanner := bufio.NewScanner(stderr)
    scanner.Split(bufio.ScanWords)
    for scanner.Scan() {
        m := scanner.Text()
        fmt.Println(m)
    }
    cmd.Wait()
}
Run Code Online (Sandbox Code Playgroud)

ffmpeg的版本详述如下.

ffmpeg version 3.0.2 Copyright (c) 2000-2016 the FFmpeg developers
built with Apple LLVM version 7.3.0 (clang-703.0.29)
configuration: --prefix=/usr/local/Cellar/ffmpeg/3.0.2 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libxvid --enable-vda
libavutil      55. 17.103 / 55. 17.103
libavcodec     57. 24.102 / 57. 24.102
libavformat    57. 25.100 / 57. 25.100
libavdevice    57.  0.101 / 57.  0.101
libavfilter     6. 31.100 /  6. 31.100
libavresample   3.  0.  0 /  3.  0.  0
libswscale      4.  0.100 /  4.  0.100
libswresample   2.  0.101 /  2.  0.101
libpostproc    54.  0.100 / 54.  0.100
Run Code Online (Sandbox Code Playgroud)


sea*_*est 4

我确实发现他在那篇文章中提到的 icza 解决方案非常有用,但它并没有解决我的问题。

我做了一个小测试如下:

1,我写了一个脚本,每秒打印一些信息十次,这是 script.sh

#!/bin/bash

for i in {1..10}
do
    echo "step " $i
    sleep 1s
done
Run Code Online (Sandbox Code Playgroud)

2、读取stdout并从stdout中提取所需的信息并进行一些处理以获得预期的格式,代码如下:package main

import (
    "fmt"
    "os/exec"
    "regexp"
    "strconv"
    "strings"
)

func getRatio(text string) float32 {
    re1, _ := regexp.Compile(`step[\s]+(\d+)`)
    result := re1.FindStringSubmatch(text)
    val, _ := strconv.Atoi(result[1])
    return float32(val) / 10
}

func main() {
    cmdName := "ffmpeg -i t.webm  -acodec aac -vcodec libx264  cmd1.mp4"
    //cmdName := "bash ./script.sh"
    cmdArgs := strings.Fields(cmdName)

    cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
    stdout, _ := cmd.StdoutPipe()
    cmd.Start()

    oneByte := make([]byte, 10)
    for {
        _, err := stdout.Read(oneByte)
        if err != nil {
            break
        }
        progressingRatio := getRatio(string(oneByte))
        fmt.Printf("progressing  ratio %v \n", progressingRatio)
    }
}
Run Code Online (Sandbox Code Playgroud)

这确实适用于我的 script.sh 测试,但对于 ffmpeg 命令它不起作用,在 ffmpeg 的情况下,没有打印任何内容并且过程完成(没有卡住),我想将数据写入 ffmpeg 的标准输出的方式是有点特别(也许根本没有换行符,我尝试了icza的解决方案,但它仍然不起作用)。