Bash: registrar stdout y stderr a la vez que se conservan el orden y la procedencia
Es bastante simple registrar tanto el stdout como el stderr de un comando en un archivo de registro:
./foo.sh &> log.txt
El problema es que al inspeccionar el archivo de registro, uno ya no sabe qué línea proviene de qué flujo. Esto podría solucionarse redirigiendo stdout y stderr a dos archivos separados, pero luego se pierde la cronología y el intercalado de la salida.
Otra solución sería redirigir a tres archivos. Uno con el stdout, uno con el stderr, y uno con ambos combinados. Algo como:
./foo.sh 2> >(tee stderr | tee -a combined) 1> >(tee stdout | tee -a combined)
Pero no es tan elegante tener tantos archivos (y este comando aún descarga una copia de la salida en el shell).
Encontré una función de bash interesante que colorearía solo los mensajes de stderr en rojo:
color()(set -o pipefail;"$@" 2>&1>&3|sed pero no conserva el orden de la salida y el resultado es ilegible en un editor de texto. Dado el siguiente programa parafoo.sh
:
for i in 1 2; do
for j in 1 2; do
printf '%s\n' "out $i"
done
for k in 1 2; do
printf '%s\n' "err $i" >&2
done
done
Corriendocolor ./foo.sh
produce:
out 1
out 1
out 2
out 2
[31merr 1[m
[31merr 1[m
[31merr 2[m
[31merr 2[m
¿Cómo podría uno terminar fácilmente con algo como esto en un solo archivo de registro?
@| out 1
@| out 1
$| err 1
$| err 1
@| out 2
@| out 2
$| err 2
$| err 2
s,.*,\e[31m&\e[m,'>&2)3>&1
pero no conserva el orden de la salida y el resultado es ilegible en un editor de texto. Dado el siguiente programa parafoo.sh
:
for i in 1 2; do
for j in 1 2; do
printf '%s\n' "out $i"
done
for k in 1 2; do
printf '%s\n' "err $i" >&2
done
done
Corriendocolor ./foo.sh
produce:
out 1
out 1
out 2
out 2
[31merr 1[m
[31merr 1[m
[31merr 2[m
[31merr 2[m
¿Cómo podría uno terminar fácilmente con algo como esto en un solo archivo de registro?
@| out 1
@| out 1
$| err 1
$| err 1
@| out 2
@| out 2
$| err 2
$| err 2