Tubería STDERR vs. STDOUT

24

Según " Linux: The Complete Reference 6th Edition " (pág. 44), solo puede canalizar STDERR utilizando los |&símbolos de redireccionamiento.

He escrito un script bastante simple para probar esto:

#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2

Ejecuto este script así:

./script.sh |& sed 's:^:\t:'

Presumiblemente, solo las líneas impresas en STDERR estarán sangradas. Sin embargo, en realidad no funciona así, como veo:

    Normal Text.
    Error Text. 

¿Qué estoy haciendo mal aquí?

Naftuli Kay
fuente

Respuestas:

26

No sé qué texto usa su libro, pero el manual de bash es claro (si ya está familiarizado con las redirecciones):

Si |&se usa, el error estándar del comando1, además de su salida estándar, se conecta a la entrada estándar del comando2 a través de la tubería; es taquigrafía para 2>&1 |. Esta redirección implícita del error estándar a la salida estándar se realiza después de cualquier redirección especificada por el comando.

Entonces, si no desea mezclar la salida estándar y el error estándar, deberá redirigir la salida estándar a otro lugar. Ver Cómo grep flujo de error estándar (stderr)?

{ ./script.sh 2>&1 >&3 | sed 's:^:\t:'; } 3>&1

Sin embargo, fd 1 y 3 de script.shy sedapuntarán al destino stdout original. Si quieres ser un buen ciudadano, puedes cerrar los fd 3 que esos comandos no necesitan:

{ ./script.sh 2>&1 >&3 3>&- | sed 's:^:\t:' 3>&-; } 3>&1

bashy ksh93puede condensar el >&3 3>&-to >&3-(fd move).

Gilles 'SO- deja de ser malvado'
fuente
El manual de bash no es del todo claro para mí. No acabo de entender por qué Smth como ./script.sh > /tmp/stdout_goes_here |& grep 'grepping_script_stderr'no funciona como se pretende, es decir: redirección script.sh's stdout(que, de acuerdo con el manual del fragmento debe ocurrir primero), y luego permitir grepa procesar la secuencia de comandos de stderr. En cambio, stderry tdout` ambos terminan enstdout_goes_here
sxc731
1
@ sxc731 |&es abreviado para 2>&1 |. Entonces >/tmp/stdout_goes_here |&redirige stdout a /tmp/stdout_goes_here, luego 2>&1redirige stderr a donde sea que vaya stdout, que es /tmp/stdout_goes_here, y finalmente |no recibe ninguna entrada porque la salida del comando ha sido redirigida. Tenga en cuenta que >&1redirige a donde quiera que vaya el descriptor de archivo 1 , no a donde terminará yendo el descriptor de archivo 1 . Para canalizar solo stderr y redirigir stdout a un archivo, una forma es 2>&1 >/tmp/stdout_goes_here |.
Gilles 'SO- deja de ser malvado'
6

|&canaliza stderr a stdin, como 2>&1 |, por lo que el próximo programa obtendrá ambos en stdin.

$cat test.sh
#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2
$./test.sh | sed 's:^:\t:'
Error Text.
        Normal Text.
$ ./test.sh |& sed 's:^:\t:'
        Normal Text.
        Error Text.
Kevin
fuente
Oh, ¿entonces es básicamente equivalente a la expresión más larga runcommand 2>&1 | tee? es decir runcommand |& tee?
Naftuli Kay
Sí, son lo mismo.
Kevin
1

|&en bash es solo un atajo (no terriblemente portátil) para 2>&1 |, por lo que debería ver cada línea con sangría.

jw013
fuente