TL;DR:
$ cmd 2> >(stderr-filter >&2)
Ejemplo:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
Esto funcionará tanto en bash como en zsh. Bash es bastante omnipresente en estos días, sin embargo, si realmente necesita una solución (realmente retorcida) para POSIX sh
, entonces mira aquí.
Explicación
De lejos, la forma más fácil de hacer esto es redirigir STDERR a través de la sustitución de procesos:
La sustitución de procesos permite hacer referencia a la entrada o salida de un proceso mediante un nombre de archivo. Toma la forma de
>(list)
La lista de procesos se ejecuta de forma asíncrona y su entrada o salida aparece como un nombre de archivo.
Entonces, lo que obtienes con la sustitución de procesos es un nombre de archivo.
Al igual que podrías hacer:
$ cmd 2> filename
puedes hacer
$ cmd 2> >(filter >&2)
El >&2
filter
de redirección STDOUT de nuevo al STDERR original.
TL; DR:(bash y zsh)
$ cmd 2> >(stderr-filter >&2)
Ejemplo:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
Muchas respuestas en la red de StackExchange tienen la forma:
cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
Esto tiene una suposición incorporada:que el descriptor de archivo 3 no se usa para otra cosa.
En su lugar, utilice un descriptor de archivo con nombre y {ba,z}sh
asignará el siguiente descriptor de archivo disponible>=10:
cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'
Tenga en cuenta que los descriptores de archivos con nombre no son compatibles con POSIX sh
.
El otro problema con lo anterior es que el comando no se puede canalizar a más comandos sin volver a cambiar STDOUT y STDERR a sus valores originales.
Para permitir la canalización hacia adelante en POSIX sh
, (y aún suponiendo que FD 3 no se use) se complica:
(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1
Entonces, dada la suposición y la sintaxis retorcida de esto, es probable que sea mejor usar el bash
más simple /zsh
sintaxis que se muestra en el TL;DR anterior y se explica aquí.
demostración práctica, grepping solo stderr:
$ ls -l . noexistABC noexistXYZ
ls: cannot access 'noexistABC': No such file or directory
ls: cannot access 'noexistXYZ': No such file or directory
.:
total 4
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 bar.txt
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 foo.txt
drwxrwxr-x 2 frank frank 4096 Aug 19 12:26 someFolder
$ ( ls -l . noexistABC noexistXYZ 2>&1 >&3 3>&- | grep ABC >&2 3>&-) 3>&1
.:
ls: cannot access 'noexistABC': No such file or directory
total 4
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 bar.txt
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 foo.txt
drwxrwxr-x 2 frank frank 4096 Aug 19 12:26 someFolder
Aquí hay un ejemplo, inspirado en cómo intercambiar descriptores de archivos en bash. La salida de a.out es la siguiente, sin el prefijo 'STDXXX:'.
STDERR: stderr output
STDOUT: more regular
./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output
Citando del enlace anterior:
- Primero guarde la salida estándar como &3 (&1 se convierte en 3)
- A continuación, envíe stdout a stderr (&2 se convierte en 1)
- Enviar stderr a &3 (stdout) (&3 se convierte en 2)
- cerrar &3 (&- se convierte en 3)
Un uso ingenuo de la sustitución de procesos parece permitir el filtrado de stderr
por separado de stdout
:
:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err
Tenga en cuenta que stderr
sale en stderr
y stdout
en stdout
, que podemos ver envolviendo todo en otra subcapa y redirigiendo a los archivos o
y e
( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e