2016-05-07 4 views
3

Ich versuche, Shell-Befehl mit os/exec in Golang aufrufen, dieser Befehl wird einige Zeit dauern, so möchte ich die reatime Ausgabe abrufen und die verarbeitete Ausgabe (eine fortschreitende Verhältniszahl) drucken.Wie bekomme ich die Echtzeit-Ausgabe für einen Shell-Befehl in Golang?

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) 
} 

Ich möchte eine Funktion haben, wo Sie den Bildschirm aktualisieren, wenn der Befehl Druck etwas,

Die ffmpeg Befehlsausgabe ist wie folgt:

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/ 

in der Tat, die über 4 line ist die letzte Zeile der ffmpeg Befehlsausgabe, die sich ständig ändert, ich möchte diese Änderung ausdrucken, wie

Wie könnte ich das erreichen?

+1

Die 'fmt'-Bibliothek verfügt über eine Reihe von Scanfunktionen, die eine formatierte Zeichenfolge in Werte parsen können. Sie können einen Scanner in der Standardausgabe für Linien verwenden und jede formatierte Linie scannen. Sie müssten herausfinden, wie das Verhältnis von vollständig zu insgesamt von der ffmpeg. –

+0

Mögliche Duplikate von [Streaming-Befehle Ausgabefortschritt] (http://stackoverflow.com/questions/30725751/streaming-commands-output-progress) – icza

+0

@icza, können Sie bitte einen Blick auf meine veröffentlichte Antwort, warum ich nichts bekommen auf dem Bildschirm gedruckt? – seaguest

Antwort

1

Ich finde die Lösung von icza, die er in diesem Beitrag erwähnte, ist sehr nützlich, aber es hat mein Problem nicht gelöst.

habe ich einen kleinen Test, wie folgend:

1, ich ein Skript schreiben, das einige Informationen drucken jede Sekunde für zehn Mal, hier ist die script.sh

#!/bin/bash 

for i in {1..10} 
do 
    echo "step " $i 
    sleep 1s 
done 

2, lesen Sie die stdout und die benötigten Informationen aus stdout extrahiert und einen Prozess tun, um das erwartete Format zu bekommen, hier ist der Code: Paket Haupt

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) 
    } 
} 

Dies funktioniert für meine sc ript.sh Test, aber für den ffmpeg Befehl funktioniert es nicht, in ffmpegs Fall wird nichts gedruckt und der Prozess wird beendet (nicht stecken geblieben), ich schätze, die Art, Daten in stdout für ffmpeg zu schreiben, ist ein wenig speziell (vielleicht überhaupt kein Newline-Charakter, und ich habe versucht, Icza-Lösung, aber es funktioniert immer noch nicht).

+0

Sie könnten ffmpeg in einem Pseudo-Terminal ausführen, das würde wahrscheinlich funktionieren (?) – Hubro

1

Wenn Sie einen exec.Cmd Wert eines externen Befehl haben Sie von Go gestartet, können Sie seine Cmd.Stdin, Cmd.Stdout und Cmd.Stderr Felder verwenden, um mit dem Prozess zu kommunizieren einige Weise.

Einige Art und Weise bedeutet, dass Sie Daten an seinen Standard Eingang senden können, und Sie können den Standard und Fehlerströme lesen.

Die Belastung ist Standard. Wenn der externe Prozess Daten über eine Netzwerkverbindung sendet oder Daten in eine Datei schreibt, können Sie diese Daten nicht über die oben genannten 3 Streams abfangen.

Nun zu ffmpeg. ffmpeg und viele andere Konsolenanwendungen schreiben keine Daten in Standardausgabe/-fehler, aber sie verwenden Systemaufrufe oder andere Bibliotheken (die Systemaufrufe verwenden), um das Terminalfenster zu manipulieren. Natürlich kann eine Anwendung einige Daten an die Standardausgabe/den Standardfehler senden und andere Daten durch Manipulation des Terminalfensters anzeigen.

So sehen Sie nicht die Ausgabe von ffmpeg, weil Sie versuchen, seine Standardausgabe/Fehler zu lesen, aber ffmpeg zeigt seine Ausgabe nicht durch Schreiben in diese Streams.

Im allgemeinen Fall, wenn Sie die Ausgabe solcher Anwendungen erfassen wollen, benötigen Sie eine Bibliothek, die den (textuellen) Inhalt des Terminalfensters erfassen kann. In einer einfacheren Situation unterstützt die Anwendung das Speichern dieser Ausgaben in Dateien, die normalerweise von zusätzlichen Befehlszeilenparametern gesteuert werden, die Sie dann von Go aus lesen/überwachen können.

1

Check die unten muss Erweiterungen (nicht benutzt werden empfohlen, wie es ist), sondern arbeiten :)

package main 

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

var duration = 0 
var allRes = "" 
var lastPer = -1 

func durToSec(dur string) (sec int) { 
    durAry := strings.Split(dur, ":") 
    if len(durAry) != 3 { 
     return 
    } 
    hr, _ := strconv.Atoi(durAry[0]) 
    sec = hr * (60 * 60) 
    min, _ := strconv.Atoi(durAry[1]) 
    sec += min * (60) 
    second, _ := strconv.Atoi(durAry[2]) 
    sec += second 
    return 
} 
func getRatio(res string) { 
    i := strings.Index(res, "Duration") 
    if i >= 0 { 

     dur := res[i+10:] 
     if len(dur) > 8 { 
      dur = dur[0:8] 

      duration = durToSec(dur) 
      fmt.Println("duration:", duration) 
      allRes = "" 
     } 
    } 
    if duration == 0 { 
     return 
    } 
    i = strings.Index(res, "time=") 
    if i >= 0 { 

     time := res[i+5:] 
     if len(time) > 8 { 
      time = time[0:8] 
      sec := durToSec(time) 
      per := (sec * 100)/duration 
      if lastPer != per { 
       lastPer = per 
       fmt.Println("Percentage:", per) 
      } 

      allRes = "" 
     } 
    } 
} 

func main() { 
    os.Remove("cmd1.mp4") 
    cmdName := "ffmpeg -i 1.mp4 -acodec aac -vcodec libx264 cmd1.mp4 2>&1" 
    cmd := exec.Command("sh", "-c", cmdName) 
    stdout, _ := cmd.StdoutPipe() 
    cmd.Start() 
    oneByte := make([]byte, 8) 
    for { 
     _, err := stdout.Read(oneByte) 
     if err != nil { 
      fmt.Printf(err.Error()) 
      break 
     } 
     allRes += string(oneByte) 
     getRatio(allRes) 
    } 
} 
2

Sieht aus wie ffmpeg alle Diagnosemeldungen sendet (die „Konsolenausgabe“) zu Stderr anstelle von stdout. Der folgende Code funktioniert für mich.

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() 
} 

Die Version von ffmpeg ist wie folgt detailliert.

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 
Verwandte Themen