pipe stdout y stderr a dos procesos diferentes en shell script?


Tengo un pipline haciendo solo

 command1 | command2

Entonces, stdout de command1 va a command2, mientras que stderr de command1 va al terminal (o donde stdout del shell está).

¿Cómo puedo canalizar stderr de command1 a un tercer proceso (command3) mientras stdout todavía va a command2 ?

Author: olibre, 2012-02-02

5 answers

Use otro descriptor de archivo

{ command1 2>&3 | command2; } 3>&1 1>&2 | command3

Puede utilizar hasta 7 descriptores de archivo más: del 3 al 9.
Si desea más explicación, por favor pregunte, puedo explicar; -)

Prueba

{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'

Salida:

b2
a1

Ejemplo

Produce dos archivos de registro:
1. stderr solo
2. stderr y stdout

{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log

Si command es echo "stdout"; echo "stderr" >&2 entonces podemos probarlo así: {[14]]}

$ { { { echo out>&3;echo err>&1;}| tee err-only.log;} 3>&1;} > err-and-stdout.log
$ head err-only.log err-and-stdout.log
==> err-only.log <==
err

==> err-and-stdout.log <==
out
err
 52
Author: olibre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-11 21:26:49

La respuesta aceptada resulta en la inversión de stdout y stderr. Aquí hay un método que los preserva (ya que Googlear con ese propósito trae este post):

{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command

Aviso:

  • 3>&- es necesario para evitar que fd 3 sea heredado por command. (Ya que esto puede llevar a resultados inesperados dependiendo de lo que command hace dentro.)

Partes explicadas: {[43]]}
  1. Primera parte exterior:

    1. 3>&1 -- fd 3 para {[9] } se establece en lo que fd 1 era (es decir, stdout)
    2. 1>&2 -- fd 1 para { ... } se establece en lo que fd 2 era (es decir, stderr)
    3. | stdout_command -- fd 1 (was stdout) se canaliza a través de stdout_command
  2. La parte interna hereda los descriptores de archivo de la parte externa:

    1. 2>&1 -- fd 2 para command se establece en lo que fd 1 era (es decir, stderr según la parte exterior)
    2. 1>&3 -- fd 1 para {[6] } se establece en lo que fd 3 era (es decir, stdout según la parte exterior)
    3. 3>&- -- fd 3 para command se establece en nada (es decir, cerrado)
    4. | stderr_command -- fd 1 (was stderr) se canaliza a través de stderr_command

Ejemplo:

foo() {
    echo a
    echo b >&2
    echo c
    echo d >&2
}

{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'

Salida:

out: a
err: b
err: d
out: c

(El orden de a -> c y b -> d siempre será indeterminado porque no hay forma de sincronización entre stderr_command y stdout_command.)

 17
Author: antak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-01 02:51:33

Simplemente redirija stderr a stdout

{ command1 | command2; } 2>&1 | command3

Precaución: commnd3 también se leerá command2 stdout (si existe).
Para evitar eso, puedes descartar commnd2 stdout:

{ command1 | command2 >/dev/null; } 2>&1 | command3

Sin embargo, para mantener command2 stdout (por ejemplo, en la terminal),
entonces por favor refiérase a mi otra respuesta más compleja.

Prueba

{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'

Salida:

a2
b2
c2
----12
 13
Author: olibre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-04 13:46:13

Usando la sustitución de procesos:

command1 > >(command2) 2> >(command3)

Véase http://tldp.org/LDP/abs/html/process-sub.html para más información.

 3
Author: FuePi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-15 12:04:43

El mismo efecto se puede lograr con bastante facilidad con un fifo. No soy consciente de una sintaxis de tubería directa para hacerlo (aunque sería ingenioso ver una). Así es como puedes hacerlo con un fifo.

Primero, algo que imprime a ambos stdout y stderr, outerr.sh:

#!/bin/bash

echo "This goes to stdout"
echo "This goes to stderr" >&2

, Entonces podemos hacer algo como esto:

$ mkfifo err
$ wc -c err &
[1] 2546
$ ./outerr.sh 2>err | wc -c
20
20 err
[1]+  Done                    wc -c err

De esta manera se configura el oyente para la salida stderr primero y se bloquea hasta que tiene un escritor, lo que sucede en el siguiente comando, utilizando la sintaxis 2>err. Puedes ver que cada wc -c tiene 20 caracteres de entrada.

No te olvides de limpiar el fifo después de que hayas terminado si no quieres que se quede (es decir, rm). Si el otro comando quiere entrada en stdin y no un archivo arg, puede usar la redirección de entrada como wc -c < err también.

 1
Author: FatalError,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-02 13:46:17