2013-05-01 5 views
5

Pour le contexte, j'essaie de créer un script shell qui simplifie la sortie de la console en temps réel de ffmpeg, en affichant uniquement l'image courante en cours d'encodage. Mon objectif final est d'utiliser cette information dans une sorte d'indicateur de progression pour le traitement par lots.Suppression en temps réel du retour chariot dans le shell

Pour ceux qui ne connaissent pas la sortie de ffmpeg, il sort des informations vidéo codées pour stdout et consoler les informations vers stderr. En outre, lorsqu'il affiche les informations de codage, il utilise les retours chariot pour empêcher l'écran de la console de se remplir. Cela rend impossible d'utiliser simplement grep et awk pour capturer les informations de ligne et de trame appropriées.

La première chose que j'ai essayé est de remplacer les retours chariot en utilisant tr:

$ ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr '\r' '\n'

Cela fonctionne en ce qu'il affiche une sortie en temps réel à la console. Cependant, si je redirige cette information vers grep ou awk ou toute autre chose, la sortie de tr est tamponnée et n'est plus en temps réel. Par exemple: $ ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr '\r' '\n'>log.txt résulte dans un fichier qui est immédiatement rempli avec certaines informations, puis 5-10 secondes plus tard, plus de lignes sont déposées dans le fichier journal. Au début, je pensais que sed serait bon pour cela: $ # ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | sed 's/\\r/\\n/', mais il arrive à la ligne avec tous les retours chariot et attend jusqu'à ce que le traitement est terminé avant de tenter de faire quoi que ce soit. Je suppose que c'est parce que sed travaille ligne par ligne et a besoin que toute la ligne soit complétée avant de faire quoi que ce soit d'autre, et ensuite elle ne remplace pas les retours chariot de toute façon. J'ai essayé différentes regex pour le retour chariot et la nouvelle ligne, et je n'ai pas encore trouvé de solution qui remplace le retour chariot. J'utilise OSX 10.6.8, donc j'utilise BSD sed, ce qui pourrait expliquer cela.

J'ai également essayé d'écrire l'information dans un fichier journal et d'utiliser tail -f pour le relire, mais je rencontre toujours le problème du remplacement des retours chariot en temps réel.

J'ai vu qu'il existe des solutions pour cela en python et perl, cependant, je suis réticent à suivre cette voie immédiatement. Premièrement, je ne connais pas python ou perl. Deuxièmement, j'ai une application de traitement par lots complètement fonctionnelle que je devrais soit porter, soit comprendre comment intégrer python/perl. Probablement pas dur, mais pas ce que je veux entrer à moins que je doive absolument le faire. Donc, je cherche une solution shell, de préférence bash, mais n'importe quel OSX serait bien.

Et si ce que je veux n'est tout simplement pas faisable, eh bien je suppose que je traverserai ce pont quand j'arriverai là.

Répondre

4

S'il s'agit uniquement d'une mise en mémoire tampon de sortie par l'application réceptrice après le canal. Ensuite, vous pouvez essayer d'utiliser gawk (et quelques awd BSD) ou mawk qui peut vider les tampons. Par exemple, essayez:

... | gawk '1;{fflush()}' RS='\r\n' > log.txt 

Alternativement, si vous awk ne supporte pas cela, vous pouvez forcer cela en fermant à plusieurs reprises le fichier de sortie et annexant la ligne suivante ...

... | awk '{sub(/\r$/,x); print>>f; close(f)}' f=log.out 

Ou vous pouvez simplement utiliser shell, par exemple dans bash:

... | while IFS= read -r line; do printf "%s\n" "${line%$'\r'}"; done > log.out 
+0

Merci beaucoup! Votre première commande fonctionne parfaitement bien que j'utilise 'awk' depuis que je suis sur OSX. Cela me sauve tellement de maux de tête! – Seth

+1

Une chose à ajouter à cette excellente réponse: vérifiez votre documentation awk/gawk/mawk installée pour savoir comment interpréter RS. Dans ma boîte OSX locale, awk avait un OU implicite pour le séparateur d'enregistrement (soit retour chariot, soit retour à la ligne). Sur mon serveur Ubuntu, gawk 4.0.1 interprète RS comme une expression régulière s'il a plus d'un caractère. Donc, j'ai dû utiliser 'RS = '\ r | \ n'' pour obtenir le même comportement que j'ai vu sur OSX. – ajmicek

4

Libc utilise la mise en mémoire tampon de ligne lorsque stdout et stderr sont connectés à un terminal et la mise en mémoire tampon complète (avec un tampon de 4 Ko) lorsqu'elle est connectée à un canal. Cela se produit dans le processus de génération de la sortie, pas dans le processus de réception — c'est la faute de ffmpeg, dans votre cas, pas tr.

unbuffer ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr '\r' '\n' 
stdbuf -e0 -o0 ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr '\r' '\n' 

Essayez d'utiliser unbuffer or stdbuf pour désactiver mise en mémoire tampon de sortie.

+0

Je suis un peu confus par cela. Si je redirige simplement stderr vers un fichier, c'est-à-dire '2> log.txt', j'obtiens des mises à jour du fichier en temps réel (si j'utilise' tail -f' sur le fichier, je vois ce que je suppose être des mises à jour en ligne; beaucoup plus rapide que le tampon "tr" fait). En termes de 'unbuffer' et' stdbuf', je ne semble pas avoir l'un ou l'autre sur mon système, même si j'ai 'expect' qui m'a fait penser que je devrais' unbuffer'. Je veux un peu de transportabilité à mon script, donc si je dois utiliser une application non standard, je voudrais un exécutable statique qui pourrait voyager avec mes scripts. – Seth

+0

Très bien, j'ai essayé «unbuffer» en le faisant moi-même à partir des instructions [ici] (http://superuser.com/questions/59497/writing-tail-f-output-to-another-file). Cela n'obtient toujours que des mises à jour dans des blocs tamponnés et non par ligne. – Seth

1

La mise en mémoire tampon de données entre des processus dans un tuyau est contrôlée avec des limites du système, ce qui est au moins sur mon système (Fedora 17) pas possible de modifier:

$ ulimit -a | grep pipe 
pipe size   (512 bytes, -p) 8 
$ ulimit -p 1 
bash: ulimit: pipe size: cannot modify limit: Invalid argument 
$ 

Bien que cette mise en mémoire tampon est liée principalement à combien de données excédentaires le producteur est autorisé à produire avant qu'il ne soit arrêté si le consommateur ne consomme pas à la même vitesse, cela peut également affecter le moment de la livraison de plus petites quantités de données (pas tout à fait sûr de cela). C'est la mise en mémoire tampon des données de tuyau, et je ne pense pas qu'il y ait beaucoup à modifier ici. Cependant, les programmes qui lisent/écrivent les données canalisées peuvent également mettre en mémoire tampon les données stdin/stdout et ce que vous voulez éviter dans votre cas.

Voici un script perl qui devrait faire la traduction avec mise en mémoire tampon d'entrée minimale et aucune mise en mémoire tampon de sortie:

#!/usr/bin/perl 
use strict; 
use warnings; 

use Term::ReadKey; 
$ReadKeyTimeout = 10; # seconds 

$| = 1; # OUTPUT_AUTOFLUSH 

while(my $key = ReadKey($ReadKeyTimeout)) { 
     if ($key eq "\r") { 
       print "\n"; 
       next; 
     } 
     print $key; 
} 

Cependant, pointeur comme déjà, vous devez vous assurer que ffmpeg ne cache pas sa sortie si vous veulent une réponse en temps réel.