bash - saltos - Eliminación en tiempo real del retorno del carro en la carcasa
salto de linea unicode (3)
Para el contexto, intento crear un script de shell que simplifique la salida de la consola en tiempo real de ffmpeg, mostrando solo el fotograma actual que está siendo codificado. Mi objetivo final es utilizar esta información en algún tipo de indicador de progreso para el procesamiento por lotes.
Para aquellos que no están familiarizados con la salida de ffmpeg, emite información codificada de video para stdout y la información de la consola a stderr. Además, cuando realmente muestra la información de codificación, utiliza retornos de carro para evitar que la pantalla de la consola se llene. Esto hace que sea imposible simplemente usar grep y awk para capturar la línea y la información de marco apropiadas.
Lo primero que he intentado es reemplazar los retornos de carro usando tr:
$ ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr ''/r'' ''/n''
Esto funciona porque muestra salida en tiempo real a la consola. Sin embargo, si luego canalizo esa información a grep o awk o cualquier otra cosa, la salida de tr se almacena temporalmente y ya no está en tiempo real. Por ejemplo: $ ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr ''/r'' ''/n''>log.txt
$ ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | tr ''/r'' ''/n''>log.txt
da como resultado un archivo que se llena de inmediato con cierta información, luego 5-10 segundos después, se colocan más líneas en el archivo de registro.
Al principio pensé que sed sería genial para esto: $ # ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | sed ''s///r///n/''
$ # ffmpeg -i "ScreeningSchedule-1.mov" -y "test.mp4" 2>&1 | sed ''s///r///n/''
, pero llega a la línea con todos los retornos de carro y espera hasta que el procesamiento haya finalizado antes de intentar hacer algo. Supongo que esto se debe a que sed funciona línea por línea y necesita que toda la línea se haya completado antes de que haga cualquier otra cosa, y luego no reemplaza los retornos de carro de todos modos. He intentado varios regex diferentes para el retorno de carro y la nueva línea, y aún tengo que encontrar una solución que reemplace el retorno de carro. Estoy ejecutando OSX 10.6.8, así que estoy usando BSD sed, lo que podría explicar eso.
También intenté escribir la información en un archivo de registro y usar tail -f
para volver a leerla, pero aún me encuentro con la cuestión de reemplazar los retornos de carro en tiempo real.
He visto que hay soluciones para esto en python y perl, sin embargo, soy reacio a tomar esa ruta de inmediato. Primero, no sé Python o Perl. En segundo lugar, tengo una aplicación de shell de procesamiento por lotes completamente funcional que necesitaría ya sea para portuar o averiguar cómo integrarme con python / perl. Probablemente no sea difícil, pero no es lo que quiero entrar a menos que sea absolutamente necesario. Así que estoy buscando una solución de shell, preferiblemente bash, pero cualquiera de las shells de OSX estaría bien.
Y si lo que quiero simplemente no es factible, bueno, creo que cruzaré ese puente cuando llegue allí.
El almacenamiento en búfer de datos entre procesos en una tubería se controla con algunos límites del sistema, que al menos en mi sistema (Fedora 17) no es posible modificar:
$ ulimit -a | grep pipe
pipe size (512 bytes, -p) 8
$ ulimit -p 1
bash: ulimit: pipe size: cannot modify limit: Invalid argument
$
Aunque este almacenamiento intermedio está relacionado principalmente con la cantidad de datos excedentes que el productor puede producir antes de detenerse si el consumidor no está consumiendo a la misma velocidad, también podría afectar el tiempo de entrega de cantidades de datos más pequeñas (no estoy seguro de esto). )
Ese es el almacenamiento en búfer de los datos de la tubería, y no creo que haya mucho que modificar aquí. Sin embargo, los programas que leen / escriben los datos canalizados también pueden almacenar datos stdin / stdout y esto es lo que desea evitar en su caso.
Aquí hay una secuencia de comandos de Perl que debe hacer la traducción con un buffering de entrada mínimo y sin buffer de salida:
#!/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;
}
Sin embargo, como ya apunta, debe asegurarse de que ffmpeg no almacene su salida si desea una respuesta en tiempo real.
Si solo es una cuestión de buffer de salida por la aplicación receptora después de la tubería. Entonces podrías intentar usar gawk
(y algunos BSD awk) o mawk
que pueden descargar búferes. Por ejemplo, intente:
... | gawk ''1;{fflush()}'' RS=''/r/n'' > log.txt
Alternativamente, si awk no es compatible con esto, puede forzar esto al cerrar repetidamente el archivo de salida y agregar la siguiente línea ...
... | awk ''{sub(//r$/,x); print>>f; close(f)}'' f=log.out
O podrías usar shell, por ejemplo en bash
:
... | while IFS= read -r line; do printf "%s/n" "${line%$''/r''}"; done > log.out
Libc usa buffer de línea cuando stdout y stderr están conectados a un terminal y full-buffering (con un buffer de 4KB) cuando están conectados a un pipe. Esto sucede en el proceso que genera la salida, no en el proceso de recepción; es culpa de ffmpeg
, en su caso, no de 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''
Intente usar stdbuf
o stdbuf
para desactivar el buffer de salida.