Wie entscheidet ein Programm, ob eine farbige Ausgabe erfolgen soll oder nicht?

Wie entscheidet ein Programm, ob eine farbige Ausgabe erfolgen soll oder nicht?

Wenn ich einen Befehl von einem Terminal aus ausführe, der farbige Ausgaben druckt (wie lsodergcc ), wird die farbige Ausgabe gedruckt. Nach meinem Verständnis gibt der Prozess tatsächlich ausANSI-Escape-Codes, und das Terminal formatiert die Farbe.

Wenn ich jedoch denselben Befehl durch einen anderen Prozess (z. B. eine benutzerdefinierte C-Anwendung) ausführe und die Ausgabe auf die Ausgabe der Anwendung umleite, bleiben diese Farben nicht bestehen.

Wie entscheidet ein Programm, ob Text im Farbformat ausgegeben wird oder nicht? Gibt es dafür Umgebungsvariable?

Antwort1

Die meisten dieser Programme geben standardmäßig nur Farbcodes an ein Terminal aus. Sie prüfen, ob ihre Ausgabe ein TTY ist, indem sieisatty(3). Normalerweise gibt es Optionen, um dieses Verhalten zu überschreiben: Farben in allen Fällen deaktivieren oder Farben in allen Fällen aktivieren. Für GNU werden grepbeispielsweise --color=neverFarben deaktiviert und --color=alwaysaktiviert.

In einer Shell können Sie den gleichen Test durchführen mit dem-t testOperator: [ -t 1 ]ist nur erfolgreich, wenn die Standardausgabe ein Terminal ist.

Antwort2

Gibt es eine Umgebungsvariable?

Ja. Es ist die TERMUmgebungsvariable. Dies liegt daran, dass im Entscheidungsprozess mehrere Dinge verwendet werden.

Es ist schwierig, hier zu verallgemeinern, da nicht alle Programme einem einzigen Entscheidungsflussdiagramm zustimmen. Tatsächlich grepist GNU , das in M. Kitts Antwort erwähnt wird, ein gutes Beispiel für einen Ausreißer, der einen etwas ungewöhnlichen Entscheidungsprozess mit unerwarteten Ergebnissen verwendet. Ganz allgemein ausgedrückt gilt daher:

  • Die Standardausgabe muss ein Terminalgerät sein, wie von bestimmt isatty().
  • Das Programm muss in der Lage sein, den Datensatz für den Terminaltyp in der Termcap/Terminfo-Datenbank nachzuschlagen.
  • Deshalb muss esSeiein nachzuschlagender Terminaltyp. Die TERMUmgebungsvariable muss vorhanden sein und ihr Wert muss mit einem Datenbankeintrag übereinstimmen.
  • Es muss daher eine terminfo/termcap-Datenbank geben. Bei einigen Implementierungen des Subsystems kann der Speicherort der termcap-Datenbank mithilfe einer TERMCAPUmgebungsvariable angegeben werden. Bei einigen Implementierungen gibt es also einezweiteUmgebungsvariable.
  • Der Termcap/Terminfo-Eintrag muss angeben, dass der Terminaltyp Farben unterstützt. max_colorsIn Terminfo gibt es ein Feld. Es ist nicht für Terminaltypen festgelegt, die eigentlich keine Farbfähigkeiten haben. Tatsächlich gibt es eine Terminfo-Konvention, dass es für jeden farbfähigen Terminaltyp einen anderen Eintrag mit -moder -monoan den Namen angehängt gibt, der keine Farbfähigkeit angibt.
  • Der Termcap/Terminfo-Datensatz muss dem Programm die Möglichkeit bieten, Farben zu ändern. In Terminfo gibt es set_a_foregroundund -Felder.set_a_background

Es ist etwas komplexer als nur zu prüfenisatty() . Es bestehtweiterdurch mehrere Dinge kompliziert:

  • MancheAnwendungen fügen Befehlszeilenoptionen oder Konfigurationsflags hinzu, die dieisatty() Prüfung außer Kraft setzen, so dass das Programmstetsoderniemalssetzt voraus, dass es ein (färbbares) Terminal als Ausgabe hat. Beispiele:
    • GNU lsverfügt über eine --colorBefehlszeilenoption.
    • BSD lsbetrachtet dieCLICOLOR (ihre Abwesenheit Bedeutungniemals) UndCLICOLOR_FORCE (seine Anwesenheit bedeutetstets) Umgebungsvariablen und bietet auch die -GBefehlszeilenoption.
  • MancheAnwendungen verwenden kein Termcap/Terminfo und haben fest verdrahtete Reaktionen auf den Wert von TERM.
  • Nicht alle Terminals verwenden ECMA-48- oder ISO 8613-6-SGR-Sequenzen, die leicht fälschlicherweise „ANSI-Escape-Sequenzen“ genannt werden, um Farben zu ändern. Der Termcap/Terminfo-Mechanismus ist tatsächlich so konzipiert, dass er Anwendungen vor der direkten Kenntnis der genauen Steuersequenzen schützt. (Darüber hinaus gibt es ein Argument, dassniemandverwendet ISO 8613-6 SGR-Sequenzen, weilalle sind sich über den Fehler einigder Verwendung eines Semikolons als Trennzeichen für RGB-Farb-SGR-Sequenzen. Der Standard gibt eigentlich einen Doppelpunkt vor.)

Wie erwähnt grepweist GNU tatsächlich einige dieser zusätzlichen Komplexitäten auf. Es konsultiert nicht termcap/terminfo, verdrahtet die auszugebenden Steuersequenzen fest und verdrahtet eine Antwort auf die TERMUmgebungsvariable.

DerDer Linux/Unix-Port davon hat diesen Code, das die Farbgebung nur aktiviert, wenn die TERMUmgebungsvariable vorhanden ist und ihr Wert nicht mit dem fest verdrahteten Namen übereinstimmt dumb:

int
sollte_einfärben (ungültig)
{
  char const *t = getenv ("TERM");
  return t und strcmp (t, "dumm") != 0;
}

Auch wenn Ihr also TERMist xterm-mono, wird GNU grepentscheiden, Farben auszugeben, obwohl andere Programme wie vimdies nicht tun.

DerDer Win32-Port davon hat diesen Code, wodurch die Farbgebung entweder dann aktiviert wird, wenn die TERMUmgebungsvariablenichtexistiert oder wenn es existiert und sein Wert nicht mit dem fest verdrahteten Namen übereinstimmt dumb:

int
sollte_einfärben (ungültig)
{
  char const *t = getenv ("TERM");
  Rückgabe ! (t und strcmp (t, "dumm") == 0);
}

GNUs grepProbleme mit Farbe

Die Farbgebung von GNU grepist tatsächlich berüchtigt. Da es die Terminalausgabe nicht richtig erstellt, sondern einfach an verschiedenen Stellen in seiner Ausgabe ein paar fest verdrahtete Steuersequenzen einfügt, in der vergeblichen Hoffnung, dass das gut genug ist, zeigt es unter bestimmten Umständen tatsächlich eine falsche Ausgabe an.

Unter diesen Umständen muss etwas am rechten Rand des Terminals eingefärbt werden. Programme, die die Terminalausgabe ordnungsgemäß ausführen, müssen automatische rechte Ränder berücksichtigen. Zusätzlichbis hin zur geringen Möglichkeit, dass das Terminal sie nicht hat (nämlich dieauto_right_margin Feld in terminfo), folgt das Verhalten von Terminals, die automatische rechte Ränder haben, oft dem DEC VT-Präzedenzfall vonausstehender Zeilenumbruch. GNUgrep berücksichtigt dies nicht und erwartet naivsofortiger Zeilenumbruch, und die farbige Ausgabe geht schief.

Farbige Ausgabe ist keine einfache Sache.

Weiterführende Literatur

Antwort3

Derunbuffer Befehl aus demerwartenPaket entkoppelt die Ausgabe des ersten Programms und die Eingabe des zweiten Programms.

Sie würden es folgendermaßen verwenden:

unbuffer myshellscript.sh | grep value

Ich benutze es ständig mit Ansible und einem HomebrewedcteeSkript, damit ich die Farbausgabe auf dem Terminal sehen kann, während die Protokolldatei die normale (nicht farbige) Ausgabe behält.

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log

verwandte Informationen