Zeigen Sie stdout und stderr in zwei separaten Streams an

12

Ich suche nach einer Möglichkeit, stdout und stderr visuell zu trennen, damit sie nicht ineinander greifen und einfach identifiziert werden können. Im Idealfall haben stdout und stderr separate Bereiche auf dem Bildschirm, in denen sie angezeigt werden, z. B. in verschiedenen Spalten. Zum Beispiel eine Ausgabe, die so ausgesehen hätte:

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

würde stattdessen so aussehen:

~$ some command          |
some useful output info  |
more output              |  ERROR: an error
another message          |  ERROR: has occurred
~$                       |
Zoey Hewll
quelle
Diese Frage scheint nicht dasselbe zu stellen, und keine der Antworten enthält die hier gestellten Fragen.
Michael Homer
2
Wäre es sinnvoll, die Streams in zwei verschiedene Protokolldateien umzuleiten und sie dann mit so etwas wie MultiTail zu bearbeiten? vanheusden.com/multitail
Kusalananda
Sieht das Hilfsprogramm für die Ausgabe von Anmerkungen nützlich aus, oder benötigen Sie die Ausgabe in Spalten?
Jeff Schaller

Antworten:

4

Sie könnten die screenvertikale Teilungsfunktion von GNU verwenden :

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

Zum Beispiel als:

that-script 'ls / /not-here'

Die Idee ist, dass der Bildschirm mit einer temporären Konfigurationsdatei ausgeführt wird, die zwei Bildschirmfenster in einem vertikal geteilten Layout startet. Im ersten führen wir Ihren Befehl mit dem mit dem zweiten verbundenen stderr aus.

Wir verwenden eine Named Pipe für das zweite Fenster, um dem ersten sein tty-Gerät mitzuteilen, und für das erste, um dem zweiten mitzuteilen, wann der Befehl ausgeführt wurde.

Der andere Vorteil gegenüber Pipe-basierten Ansätzen besteht darin, dass stdout und stderr des Befehls weiterhin mit tty-Geräten verbunden sind, sodass die Pufferung nicht beeinträchtigt wird. Beide Fenster können auch unabhängig voneinander nach oben und unten gescrollt werden (im Kopiermodus screen).

Wenn Sie eine Shell wie bashinteraktiv mit diesem Skript ausführen , werden Sie feststellen, dass die Eingabeaufforderung im zweiten Fenster angezeigt wird, während die Shell liest, was Sie im ersten Fenster eingeben, während diese Shells ihre Eingabeaufforderung auf stderr ausgeben.

Im Fall von bashwird das Echo Ihrer Eingabe auch im zweiten Fenster angezeigt, da dieses Echo von der Shell (im Fall von readline bash) auch auf stderr ausgegeben wird . Bei einigen anderen Shells mögen ksh93, zeigt es auf dem ersten Fenster ( Echo Ausgang vom Terminal Gerätetreiber, nicht die Shell), wenn Sie die Schale in setzen emacsoder viModus mit set -o emacsoder set -o vi.

Stéphane Chazelas
quelle
1

Dies ist eine hässliche Lösung, die auf dem annotate-outputSkript von Debian ANNOTATE-OUTPUT (1) basiert . Ich bin mir nicht sicher, ob dies das ist, wonach Sie suchen, aber es könnte etwas sein, mit dem Sie beginnen:

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

Sie können es mit ./this_script another_scriptoder testen command.

Kaffeebecher
quelle
1

Ich werde versuchen, den folgenden Teil Ihrer Frage zu analysieren:

würde stattdessen so aussehen:

 ~ $ irgendein Befehl
 einige nützliche Ausgabeinformationen |
 mehr Leistung | FEHLER: ein Fehler
 eine andere Nachricht | Ein Fehler ist aufgetreten
 ~ $ 

Wenn man aufteilen wollte, was Sie wollen, ist:

1) Der stdoutStream würde nicht jede Zeile mit einem, CR LFsondern mit einem '|' Charakter. Dies würde die beiden Ströme natürlich nicht zusammen ausrichten, und eine Ausrichtung kommt nicht in Frage, da die Länge zukünftiger Zeilen, die zu den Strömen hinzugefügt werden, vorhergesagt werden müsste stdout, was natürlich unmöglich ist.

2) Unter der Annahme, dass wir die Ausrichtung vergessen haben, geben wir das Ergebnis einfach aus, stderrnachdem es von einer Pipeline verarbeitet wurde, die am Anfang jeder Zeile "ERROR:" hinzufügt. Ich nehme an, dass dies recht einfach ist, indem Sie ein einfaches Skript erstellen und sicherstellen, dass es stderrimmer durch dieses Skript geht.

Aber das würde eine Ausgabe wie diese erzeugen:

~ $ irgendein Befehl
 einige nützliche Ausgabeinformationen |
 mehr Leistung | FEHLER: ein Fehler
 eine andere Nachricht | Ein Fehler ist aufgetreten

Was ist nicht wirklich hilfreich, oder? Auch ich glaube nicht, es ist das, was du auch suchst!

Das Problem mit der anfänglichen Frage ist meiner Meinung nach, dass Sie die serielle Natur jeder in einem Stream angehängten Zeile nicht berücksichtigen, in Verbindung mit der Tatsache, dass beide Streams möglicherweise asynchron geschrieben werden.

Ich glaube, die nächstmögliche Lösung wäre die Verwendung ncurses.
Sehen.
[ http://www.tldp.org/HOWTO/html_single/NCURSES-Programming-HOWTO/]
[ http://invisible-island.net/ncurses/ncurses-intro.html#updating]

Um das zu tun, was Sie wollen, müssen Sie beide Streams puffern und sie kombinieren, um einen dritten Puffer zu erstellen, der Elemente aus beiden Puffern übernimmt. Entleeren Sie dann den dritten Puffer im Terminalbildschirm, indem Sie den Terminalbildschirm löschen und ihn bei jedem Wechsel des dritten Puffers neu streichen. Aber so ncursesfunktioniert es. Warum also das Rad neu erfinden und nicht von dort aufgreifen?
In jedem Fall müssten Sie übernehmen, dass der Terminalbildschirm komplett lackiert ist ! Richten Sie den Text in der neu gedruckten Version des Bildschirms nach Ihren Wünschen aus. Ähnlich wie ein Videospiel mit Terminalcharakteren.
Ich hoffe, meine Antwort wird hilfreich sein, um die Grenzen Ihrer Sucht zu klären ...
Entschuldigen Sie, dass ich dies wiederhole, aber das größte Problem bei dem, was Sie gezeigt haben, ist, wie der "Prozessor" der stdoutund der stderrStreams die Länge der hinzugefügten zukünftigen Zeilen im Voraus kennt, um sie richtig auszurichten.

Angelos Asonitis
quelle