Die Umleitung von stdout ignoriert Zeilen ohne Zeilenumbruch

Die Umleitung von stdout ignoriert Zeilen ohne Zeilenumbruch

Ich versuche, meine stderrim Terminal rot auszudrucken. Das folgende Skript leitet sie bei Debug 2zu einem benutzerdefinierten 8Trap um.

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "${RED}${line}${COLORRESET}"
    done
)
function undirect(){ exec 2>&9; } # reset to original 9 (==2)
function redirect(){ exec 2>&8; } # set to custom 8
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Es kommt vonHier, mit einer klaren Erklärung.

Scheint sehr gut zu funktionieren, allerdings werden Eingaben, die nicht durch eine neue Zeile abgeschlossen sind, überhaupt nicht ausgegeben. Zitat des Autorsgospeswieder:

bash> echo -en "hi\n" 1>&2
    hi       <-- this is red
bash> echo -en "hi" 1>&2
bash> echo -en "hi" 1>&2
bash> echo -en "hi\n" 1>&2
    hihihi   <-- this is red

Ich kann nicht herausfinden, warum. Der Inhalt ohne Zeilenumbruch scheint in einer Art Puffer zu landen. Entweder erreicht er nicht einmal den Dateideskriptor 8oder will irgendwie nicht sofort ausgedruckt werden. Wohin geht er? redirectwird jedes Mal richtig aufgerufen. Außerdem IFS=''bedeutet es, dass es kein Trennzeichen gibt, also verstehe ich nicht ganz, warum die Ausgabe 8zeilenweise erfolgt.

Ein Bugfix wäre sehr willkommen, ich habe die zitierte Antwort auf diese Frage verlinkt.

Diese gesamte Lösung ist, wie Gilles anmerkt, nicht ganz perfekt. Ich habe Probleme mit Lesen, Standardeingabe, Fortschrittsbalken, kann weder sunoch source. Und häufig größere Probleme wie unterbrochene Pipes und unerwartete Terminal-Exits. Wenn jemand über meinen Link hierher gelangt ist, erwäge bitte die Verwendung vonhttps://github.com/sickill/stderredstattdessen ist es viel besser (noch keine Probleme) ( echo bla >&2bleibt jedoch nicht rot undDas jeweilige Problem ist geschlossen)

Antwort1

Sie haben die Teilzeilenausgabe als Teil derselben Zeile an der Stelle erhalten, an der die neue Zeile gedruckt wurde. Die Teile der Zeile werden innerhalb gepuffert read.das ist, was es tut:

DerlesenDas Dienstprogramm soll eine einzelne logische Zeile von der Standardeingabe lesen

Dies wird beispielsweise <foobar>nach einer Sekunde gedruckt, nicht nach <foo><bar>.

(echo -n foo ; sleep 1 ; echo bar) | (read x ; echo "<$x>")

Wenn Sie Eingaben in kleineren Teilen als ganzen Zeilen erfassen möchten, müssen Sie etwas anderes tun, z. B. mit Perl. Dies würde drucken <foo><bar\n>(mit dem Zeilenumbruch vor dem letzten >, da readPerl im Gegensatz zu den letzten Zeilenumbrüchen nicht speziell behandelt. Sollte bei der Farbgebung keine Rolle spielen.)

(echo -n foo ; sleep 1 ; echo bar) | 
    perl -e '$|=1; while(sysread STDIN,$a,9999) { print "<$a>"}'

Wenn Sie die Steuercodes für Farben ( REDund COLORRESET) in die Umgebung exportiert haben, können Sie sie wie hier aus dem Perl-Skript verwenden:

perl -e '$|=1; while(sysread STDIN,$a,9999) {print "$ENV{RED}$a$ENV{COLORRESET}"}'

Antwort2

In Bash können Sie die -dOption „Builtin“ verwenden read, die das Zeilenendesymbol definiert. man bashgibt Folgendes an:

-d delim    The first character of delim is used to terminate the input line, 
            rather than newline.

Wenn es nicht definiert ist, readwartet es, bis \nes erscheint, um einen String als Zeile zu betrachten. Wenn Sie die -dOption jedoch verwenden, können Sie es NULals Trennzeichen festlegen. Natürlich müssen Sie die Eingabe dann auch mit NUL beenden.

Beispiel:

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s\n" "$line"
    done

Ausgabe:

x

y
z

Nochmal, aber jetzt steht das printfin der Schleife nicht bei .while\n

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s" "$line"
    done

Ausgabe:

x
yz(...)

Ich habe das hinzugefügt (...)und es bedeutet, dass am Ende der zweiten Zeile kein Zeilenende steht. Aber der Text wird trotzdem verarbeitet und gedruckt.

verwandte Informationen