Afficher stdout et stderr dans deux flux distincts

12

Je cherche un moyen de séparer visuellement stdout et stderr, afin qu'ils ne s'entrelacent pas et qu'ils soient facilement identifiables. Idéalement, stdout et stderr devraient avoir des zones séparées sur l'écran dans lesquelles ils sont affichés, par exemple dans différentes colonnes. Par exemple, une sortie qui aurait ressemblé à ceci:

~$ some command
some useful output info
ERROR: an error
more output
ERROR: has occurred
another message
~$ 

ressemblerait plutôt à ceci:

~$ some command          |
some useful output info  |
more output              |  ERROR: an error
another message          |  ERROR: has occurred
~$                       |
Zoey Hewll
la source
Cette question ne semble pas demander la même chose, et aucune des réponses ne fournit ce qui est demandé ici.
Michael Homer
2
Serait-il utile de rediriger les flux vers deux fichiers journaux différents, puis d'utiliser quelque chose comme MultiTail sur eux? vanheusden.com/multitail
Kusalananda
L'utilitaire d'annotation de sortie semble-t-il utile ou avez-vous besoin de la sortie en colonnes?
Jeff Schaller

Réponses:

4

Vous pouvez utiliser screenla fonction de division verticale 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"

A utiliser par exemple comme:

that-script 'ls / /not-here'

L'idée est qu'il exécute l'écran avec un fichier de configuration temporaire qui démarre deux fenêtres d'écran dans une disposition de fractionnement vertical. Dans le premier, nous exécutons votre commande avec le stderr connecté au second.

Nous utilisons un canal nommé pour la deuxième fenêtre pour communiquer son périphérique tty à la première, et également pour la première pour indiquer au second lorsque la commande est exécutée.

L'autre avantage par rapport aux approches basées sur les tuyaux est que les commandes stdout et stderr de la commande sont toujours connectées aux périphériques tty, donc cela n'affecte pas la mise en mémoire tampon. Les deux volets peuvent également défiler de haut en bas indépendamment (en utilisant screenle mode de copie de).

Si vous exécutez un shell comme bashinteractivement avec ce script, vous remarquerez que l'invite sera affichée dans la deuxième fenêtre, tandis que le shell lira ce que vous tapez dans la première fenêtre lorsque ces shells afficheront leur invite sur stderr.

Dans le cas de bash, l' écho de ce que vous tapez apparaîtra également dans la deuxième fenêtre car cet écho est également émis par le shell (readline dans le cas de bash) sur stderr. Avec quelques autres coquilles aiment ksh93, il affichera sur la première fenêtre ( écho sortie par le pilote de périphérique terminal, pas la coquille), à moins que vous mettez le shell en emacsou en vimode avec set -o emacsou set -o vi.

Stéphane Chazelas
la source
1

Ceci est une solution laide basée sur le annotate-outputscript de Debian ANNOTATE-OUTPUT (1) . Je ne sais pas si c'est ce que vous recherchez, mais cela pourrait être quelque chose pour commencer:

#!/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

Vous pouvez le tester en utilisant ./this_script another_scriptou command.

coffeMug
la source
1

Je vais essayer d'analyser la partie suivante de votre question:

ressemblerait plutôt à ceci:

 ~ $ une commande
 quelques informations de sortie utiles |
 plus de sortie | ERREUR: une erreur
 un autre message | Une erreur est survenue
 ~ $ 

Si l'on voulait décomposer ce que vous voulez, c'est:

1) Le stdoutflux ne terminerait pas chaque ligne par un CR LFmais par un '|' personnage. Cela n'alignerait pas les deux flux ensemble bien sûr, et l'alignement est hors de question car il faudrait prévoir la longueur des futures lignes ajoutées au stdout, ce qui est bien sûr impossible.

2) En supposant que nous oublions l'alignement, nous produirions alors simplement stderraprès avoir été traité par un pipeline qui ajoute "ERREUR:" au début de chaque ligne. Je suppose que c'est assez facile en faisant un script simple et en s'assurant que le stderrsort toujours par ce script.

Mais cela créerait une sortie comme celle-ci:

~ $ une commande
 quelques informations de sortie utiles |
 plus de sortie | ERREUR: une erreur
 un autre message | Une erreur est survenue

Ce qui n'est pas vraiment utile, n'est-ce pas? Je ne crois pas non plus, c'est ce que vous recherchez aussi!

Le problème avec la question initiale, je pense, c'est que vous ne tenez pas compte de la nature sérielle de chaque ligne ajoutée dans un flux, en raison du fait que les deux flux peuvent être écrits de manière asynchrone.

Je crois que la solution la plus proche possible serait d'utiliser ncurses.
Voir.
[ http://www.tldp.org/HOWTO/html_single/NCURSES-Programming-HOWTO/]
[ http://invisible-island.net/ncurses/ncurses-intro.html#updating]

Pour faire ce que vous recherchez, vous devez mettre les deux flux en mémoire tampon et les combiner pour produire un troisième tampon qui prend les éléments des deux tampons. Vider ensuite le troisième tampon dans l'écran du terminal en effaçant l'écran du terminal et en le repeignant à chaque fois que le troisième tampon change. Mais c'est comme ncursesça que ça marche, alors pourquoi réinventer la roue et ne pas la reprendre à partir de là?
Dans tous les cas, il faudrait reprendre la façon dont l'écran du terminal est entièrement peint ! Et réalignez le texte dans la version réimprimée de l'écran comme vous le souhaitez. Un peu comme un jeu vidéo avec des personnages terminaux.
J'espère que ma réponse sera utile pour clarifier les limites de ce que vous recherchez ...
Excusez-moi de répéter cela, mais le plus gros problème avec ce que vous avez montré est de savoir comment le "processeur" des flux stdoutet stderrsaura à l'avance la longueur des futures lignes qui y seront ajoutées afin de les aligner correctement.

Angelos Asonitis
la source