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 ?
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
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 porcommand
. (Ya que esto puede llevar a resultados inesperados dependiendo de lo quecommand
hace dentro.)
Partes explicadas: {[43]]}
-
Primera parte exterior:
-
3>&1
-- fd 3 para {[9] } se establece en lo que fd 1 era (es decir, stdout
)
-
1>&2
-- fd 1 para { ... }
se establece en lo que fd 2 era (es decir, stderr
)
-
| stdout_command
-- fd 1 (was stdout
) se canaliza a través de stdout_command
-
La parte interna hereda los descriptores de archivo de la parte externa:
-
2>&1
-- fd 2 para command
se establece en lo que fd 1 era (es decir, stderr
según la parte exterior)
-
1>&3
-- fd 1 para {[6] } se establece en lo que fd 3 era (es decir, stdout
según la parte exterior)
-
3>&-
-- fd 3 para command
se establece en nada (es decir, cerrado)
-
| 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
Primera parte exterior:
-
3>&1
-- fd 3 para {[9] } se establece en lo que fd 1 era (es decir,stdout
) -
1>&2
-- fd 1 para{ ... }
se establece en lo que fd 2 era (es decir,stderr
) -
| stdout_command
-- fd 1 (wasstdout
) se canaliza a través destdout_command
La parte interna hereda los descriptores de archivo de la parte externa:
-
2>&1
-- fd 2 paracommand
se establece en lo que fd 1 era (es decir,stderr
según la parte exterior) -
1>&3
-- fd 1 para {[6] } se establece en lo que fd 3 era (es decir,stdout
según la parte exterior) -
3>&-
-- fd 3 paracommand
se establece en nada (es decir, cerrado) -
| stderr_command
-- fd 1 (wasstderr
) se canaliza a través destderr_command
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: /'
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
.)
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
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.
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.
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