Estoy buscando una manera de separar visualmente stdout y stderr, para que no se intercalen y puedan identificarse fácilmente. Idealmente, stdout y stderr tendrían áreas separadas en la pantalla en las que se muestran, por ejemplo, en columnas diferentes. Por ejemplo, una salida que se vería así:
~$ some command
some useful output info
ERROR: an error
more output
ERROR: has occurred
another message
~$
en su lugar se vería así:
~$ some command |
some useful output info |
more output | ERROR: an error
another message | ERROR: has occurred
~$ |
Respuesta1
Podrías usar screen
la función de división vertical de GNU:
#! /bin/bash -
tmpdir=$(mktemp -d) || exit
trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP
FIFO=$tmpdir/FIFO
mkfifo "$FIFO" || exit
conf=$tmpdir/conf
cat > "$conf" << 'EOF' || exit
split -v
focus
screen -t stderr sh -c 'tty > "$FIFO"; read done < "$FIFO"'
focus
screen -t stdout sh -c 'read tty < "$FIFO"; eval "$CMD" 2> "$tty"; echo "[Command exited with status $?, press enter to exit]"; read prompt; echo done > "$FIFO"'
EOF
CMD="$*"
export FIFO CMD
screen -mc "$conf"
Para usar por ejemplo como:
that-script 'ls / /not-here'
La idea es que ejecute la pantalla con un archivo conf temporal que inicie dos ventanas de pantalla en un diseño dividido verticalmente. En el primero, ejecutamos tu comando con el stderr conectado al segundo.
Usamos una canalización con nombre para que la segunda ventana comunique su dispositivo tty a la primera, y también para que la primera le diga a la segunda cuando finaliza el comando.
La otra ventaja en comparación con los enfoques basados en tuberías es que stdout y stderr del comando todavía están conectados a dispositivos tty, por lo que no afecta el almacenamiento en búfer. Ambos paneles también se pueden desplazar hacia arriba y hacia abajo de forma independiente (usando screen
el modo de copia).
Si ejecuta un shell de forma bash
interactiva con ese script, notará que el mensaje se mostrará en la segunda ventana, mientras que el shell leerá lo que escriba en la primera ventana a medida que esos shells muestren su mensaje en stderr.
En el caso de bash
, elecode lo que escriba también aparecerá en la segunda ventana, ya queecobash
También lo genera el shell (readline en el caso de ) en stderr. Con algunos otros shells como ksh93
, se mostrará en la primera ventana (ecosalida del controlador del dispositivo terminal, no del shell), a menos que coloque el shell en modo emacs
o vi
con set -o emacs
o set -o vi
.
Respuesta2
Esta es una fea solución basada en el annotate-output
script de Debian.ANOTAR-SALIDA(1). No estoy seguro de si esto es lo que estás buscando, pero podría ser algo con lo que empezar:
#!/bin/bash
readonly col=150 # column to start error output
add_out ()
{
while IFS= read -r line; do
echo "$1: $line"
done
if [ ! -z "$line" ]; then
echo -n "$1: $line"
fi
}
add_err ()
{
while IFS= read -r line; do
printf "%*s %s %s: %s\n" $col "|" "$1" "$line"
done
if [ ! -z "$line" ]; then
printf "%*s %s: %s" $col "$1" "$line"
fi
}
cleanup() { __st=$?; rm -rf "$tmp"; exit $__st; }
trap cleanup 0
trap 'exit $?' 1 2 13 15
tmp=$(mktemp -d --tmpdir annotate.XXXXXX) || exit 1
OUT=$tmp/out
ERR=$tmp/err
mkfifo $OUT $ERR || exit 1
add_out OUTPUT < $OUT &
add_err ERROR < $ERR &
echo "I: Started $@"
"$@" > $OUT 2> $ERR ; EXIT=$?
rm -f $OUT $ERR
wait
echo "I: Finished with exitcode $EXIT"
exit $EXIT
Puedes probarlo usando ./this_script another_script
o command
.
Respuesta3
Intentaré analizar la siguiente parte de tu pregunta:
en su lugar se vería así:
~$ algún comando alguna información de salida útil | más producción | ERROR: un error otro mensaje | Un error ha ocurrido ~$
Si uno quisiera desglosar lo que quiere es:
1) La stdout
secuencia no terminaría cada línea con un CR LF
sino con un '|' personaje. Por supuesto, esto no alinearía las dos corrientes, y la alineación está fuera de discusión porque tendría que predecir la longitud de las líneas futuras agregadas al archivo stdout
, lo cual, por supuesto, es imposible.
2) Suponiendo que nos olvidamos de la alineación, simplemente generaríamos el resultado stderr
después de ser procesado por una tubería que agrega "ERROR:" al comienzo de cada línea. Supongo que esto es bastante fácil haciendo un script simple y asegurándose de que stderr
siempre salga a través de este script.
Pero eso crearía una salida como esta:
~$ algún comando alguna información de salida útil | más producción | ERROR: un error otro mensaje| Un error ha ocurrido
Lo cual no es realmente útil, ¿verdad? Además, no lo creo, ¡es lo que tú también buscas!
Creo que el problema con la pregunta inicial es que no se tiene en cuenta la naturaleza serial de cada línea agregada en una secuencia, en relación con el hecho de que ambas secuencias pueden escribirse de forma asincrónica.
Creo que la solución más cercana posible sería utilizar ncurses
.
Ver.
[http://www.tldp.org/HOWTO/html_single/NCURSES-Programming-HOWTO/]
[http://invisible-island.net/ncurses/ncurses-intro.html#updating]
Para hacer lo que busca, necesita almacenar en buffer ambos flujos y combinarlos para producir un tercer buffer que tome elementos de ambos buffers. Luego, descargue el tercer búfer en la pantalla del terminal borrando la pantalla del terminal y volviéndola a pintar cada vez que cambie el tercer búfer. Pero así es como ncurses
funciona, así que ¿por qué reinventar la rueda y no empezar desde ahí?
En cualquier caso, habría quehacerse cargo de la forma en que se pinta la pantalla del terminal por completo! Y realinee el texto en la versión reimpresa de la pantalla como desee. Muy parecido a un videojuego con personajes terminales.
Espero que mi respuesta sea útil para aclarar las limitaciones de lo que busca...
Disculpe por repetir esto, pero el mayor problema con lo que mostró es cómo sabrá el "procesador" de las stdout
transmisiones y stderr
de antemano la duración de la Se le agregarán líneas futuras para alinearlas correctamente.