Estoy usando ffmpeg para obtener la metainformación de un clip de audio. Pero no puedo entenderlo.
$ ffmpeg -i 01-Daemon.mp3 |grep -i Duration
FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
configuration: --prefix=/usr --bindir=/usr/bin
--datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
--mandir=/usr/share/man --Arch=i386 --extra-cflags=-O2
...
Verifiqué, esta salida de ffmpeg está dirigida a stderr.
$ ffmpeg -i 01-Daemon.mp3 2> /dev/null
Así que creo que grep no puede leer la secuencia de errores para detectar líneas coincidentes. ¿Cómo podemos habilitar grep para leer la secuencia de error?
Usando el enlace nixCraft , redirigí el flujo de error estándar al flujo de salida estándar, luego grep funcionó.
$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s
Pero, ¿qué pasa si no queremos redirigir stderr a stdout?
Si está utilizando bash
, ¿por qué no emplear tuberías anónimas, en esencia, abreviatura de lo que dijo phunehehe:
ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)
Ninguno de los shells habituales (incluso zsh) permite tuberías que no sean stdout a stdin. Pero todos los shells de estilo Bourne admiten la reasignación de descriptores de archivo (como en 1>&2
). Por lo tanto, puede desviar temporalmente stdout a fd 3 y stderr a stdout, y luego volver a colocar fd 3 en stdout. Si stuff
produce alguna salida en stdout y otra salida en stderr, y desea aplicar filter
en la salida de error dejando intacta la salida estándar, puede usar { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
.
$ stuff () {
echo standard output
echo more output
echo standard error 1>&2
echo more error 1>&2
}
$ filter () {
grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error
Esto es similar al "truco del archivo temporal" de phunehehe, pero en su lugar utiliza una canalización con nombre, lo que le permite obtener resultados un poco más cercanos a cuando salen, lo que puede ser útil para comandos de ejecución prolongada:
$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe
En esta construcción, stderr se dirigirá a la tubería llamada "mypipe". Como grep
ha sido llamado con un argumento de archivo, no buscará en STDIN su entrada. Desafortunadamente, aún tendrá que limpiar esa tubería con nombre una vez que haya terminado.
Si está utilizando Bash 4, hay una sintaxis de acceso directo para command1 2>&1 | command2
, cual es command1 |& command2
. Sin embargo, creo que esto es puramente un acceso directo de sintaxis, todavía está redirigiendo STDERR a STDOUT.
Las respuestas de Gilles y Stefan Lasiewski son buenas, pero de esta manera es más simple:
ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"
Asumo que no quieres ffmpeg's
stdout impreso.
Cómo funciona:
Vea a continuación el script utilizado en estas pruebas.
Grep solo puede operar en stdin, por lo tanto, debe convertir la secuencia stderr en una forma que Grep pueda analizar.
Normalmente, stdout y stderr se imprimen en su pantalla:
$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr
Para ocultar stdout, pero aún imprimir stderr, haga esto:
$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr
¡Pero grep no funcionará en stderr! Es de esperar que el siguiente comando suprima las líneas que contienen 'err', pero no lo hace.
$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr
Aquí está la solución.
La siguiente sintaxis de Bash ocultará la salida a stdout, pero aún mostrará stderr. Primero canalizamos stdout a/dev/null, luego convertimos stderr a stdout, porque las tuberías Unix solo funcionarán en stdout. Todavía puedes grep el texto.
$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr
(Tenga en cuenta que el comando anterior es diferente luego ./command >/dev/null 2>&1
, que es un comando muy común).
Aquí está la secuencia de comandos utilizada para las pruebas. Esto imprime una línea en stdout y una línea en stderr:
#!/bin/sh
# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2
exit 0
Puede intercambiar las transmisiones. Esto le permitiría grep
la secuencia de error estándar original mientras obtiene la salida que originalmente fue a la salida estándar en el terminal:
somecommand 3>&2 2>&1 1>&3- | grep 'pattern'
Esto funciona creando primero un nuevo descriptor de archivo (3) abierto para la salida y configurándolo en la secuencia de error estándar (3>&2
). Luego redirigimos el error estándar a la salida estándar (2>&1
). Finalmente, la salida estándar se redirige al error estándar original y se cierra el nuevo descriptor de archivo (1>&3-
).
En tu caso:
ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration
Probándolo:
$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error
$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output
Cuando canaliza la salida de un comando a otro (usando |
), solo está redirigiendo la salida estándar. Entonces eso debería explicar por qué
ffmpeg -i 01-Daemon.mp3 | grep -i Duration
no emite lo que quería (sin embargo, funciona).
Si no desea redirigir la salida de error a la salida estándar, puede redirigir la salida de error a un archivo, luego grep más tarde
ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
bash
puede redirigir stdout a la secuencia stdin a través de una tubería normal - |
También puede redirigir stdout y stderr a stdin por |&
Me gusta usar el rc
Shell en este caso.
Primero instale el paquete (es inferior a 1 MB).
Este es un ejemplo de cómo descartaría stdout
y pipe stderr
para grep rc
:
find /proc/ >[1] /dev/null |[2] grep task
Puedes hacerlo sin salir de Bash:
rc -c 'find /proc/ >[1] /dev/null |[2] grep task'
Como habrás notado, la sintaxis es sencilla, lo que hace que esta sea mi solución preferida.
Puede especificar qué descriptor de archivo desea canalizar entre paréntesis, justo después del carácter de canalización.
Los descriptores de archivo estándar están numerados como tales:
Una variación en el ejemplo de subproceso bash:
haga eco de dos líneas para stderr y tee stderr a un archivo, coloque el tee y la tubería nuevamente en stdout
(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)
stdout:
fff
some.load.errors (por ejemplo, stderr):
asdf
fff