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

17

Wenn ich einen Befehl über ein Terminal ausführe, das eine farbige Ausgabe druckt (z. B. lsoder gcc), wird die farbige Ausgabe gedruckt. Nach meinem Verständnis gibt der Prozess tatsächlich ANSI-Escape-Codes aus , und das Terminal formatiert die Farbe.

Wenn ich jedoch denselben Befehl von einem anderen Prozess (z. B. einer benutzerdefinierten C-Anwendung) ausführe und die Ausgabe auf die Ausgabe der Anwendung umleitung, bleiben diese Farben nicht erhalten.

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

Chris Smith
quelle

Antworten:

25

Die meisten dieser Programme geben standardmäßig nur Farbcodes an ein Terminal aus. sie prüfen mit, ob ihre Ausgabe ein TTY ist isatty(3). Es gibt normalerweise Optionen, um dieses Verhalten zu überschreiben: Deaktivieren Sie Farben in allen Fällen oder aktivieren Sie Farben in allen Fällen. Deaktiviert grepfür GNU beispielsweise --color=neverFarben und --color=alwaysaktiviert sie.

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

Stephen Kitt
quelle
Gibt es eine Möglichkeit, die gestartete Anwendung dahingehend zu täuschen, dass der Prozess eine Tty ist?
Chris Smith
4
Gefragt und beantwortet unter unix.stackexchange.com/questions/249723 bereits chris13523. Kommentare sind übrigens nicht wirklich der richtige Ort für Folgefragen.
JdeBP
1
@ chris13524 siehe Link von JdeBP; Sie können Programme in vielen Fällen auch zur Ausgabe von Farbcodes zwingen (siehe meine aktualisierte Antwort).
Stephen Kitt
13

Gibt es eine Umgebungsvariable?

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

Eine Verallgemeinerung ist hier schwierig, da sich nicht alle Programme auf ein einziges Entscheidungsflussdiagramm einigen. In der Tat ist GNU grep, wie in M. Kitts Antwort erwähnt, ein gutes Beispiel für einen Ausreißer, der einen etwas ungewöhnlichen Entscheidungsprozess mit unerwarteten Ergebnissen anwendet. Ganz allgemein gesagt:

  • Die Standardausgabe muss ein Endgerät sein, wie von festgelegt isatty().
  • Das Programm muss in der Lage sein, den Datensatz für den Terminaltyp in der termcap / terminfo-Datenbank nachzuschlagen.
  • Es muss also einen Terminaltyp geben, nach dem gesucht werden kann. Die TERMUmgebungsvariable muss vorhanden sein und ihr Wert muss mit einem Datenbankdatensatz übereinstimmen.
  • Es muss daher eine terminfo / termcap-Datenbank vorhanden sein. Bei einigen Implementierungen des Subsystems kann der Speicherort der Termcap-Datenbank mithilfe einer TERMCAPUmgebungsvariablen angegeben werden. Daher gibt es bei einigen Implementierungen eine zweite Umgebungsvariable.
  • Der Datensatz termcap / terminfo muss angeben, dass der Terminaltyp Farben unterstützt. Es gibt ein max_colorsFeld in terminfo. Es ist nicht für Terminaltypen festgelegt, die nicht über Farbfunktionen verfügen. In der Tat gibt es eine Terminfo-Konvention, die besagt, dass für jeden farbbaren Terminaltyp ein weiterer Datensatz mit -moder -monoan den Namen angehängt wird, der keine Farbfähigkeit angibt.
  • Der Datensatz termcap / terminfo muss dem Programm die Möglichkeit geben, die Farben zu ändern. Es gibt set_a_foregroundund set_a_backgroundFelder in terminfo.

Es ist ein bisschen komplexer als nur zu überprüfen isatty(). Es wird durch verschiedene Dinge noch komplizierter:

  • Einige Anwendungen fügen Befehlszeilenoptionen oder Konfigurationsflags hinzu, die die isatty()Prüfung außer Kraft setzen , sodass das Programm immer oder nie davon ausgeht, dass es ein (färbbares) Terminal als Ausgabe hat. Zum Beispiel:
    • GNU lshat die --colorBefehlszeilenoption.
    • BSD lsuntersucht die Umgebungsvariablen CLICOLOR(Abwesenheit bedeutet nie ) und CLICOLOR_FORCE(Anwesenheit bedeutet immer ) und bietet auch die -GBefehlszeilenoption an.
  • Einige Anwendungen verwenden termcap / terminfo nicht und haben fest verdrahtete Antworten auf den Wert von TERM.
  • Nicht alle Terminals verwenden ECMA-48- oder ISO 8613-6-SGR-Sequenzen, die leicht mit dem falschen Namen "ANSI-Escape-Sequenzen" versehen sind, um die Farben zu ändern. Der Termcap / Terminfo-Mechanismus wurde entwickelt, um Anwendungen von der direkten Kenntnis der genauen Steuersequenzen zu isolieren. (Darüber hinaus gibt es ein Argument dafür, dass niemand SGR-Sequenzen nach ISO 8613-6 verwendet, da sich alle über den Fehler einig sind, Semikolon als Trennzeichen für RGB-Farb-SGR-Sequenzen zu verwenden. Die Norm gibt tatsächlich Doppelpunkte an.)

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

Der Linux / Unix-Port hat diesen Code , der die Einfärbung nur ermöglicht, wenn die TERMUmgebungsvariable existiert und ihr Wert nicht mit dem festverdrahteten Namen übereinstimmt dumb:

int
should_colorize (nichtig)
{
  char const * t = getenv ("TERM");
  return t && strcmp (t, "dumm")! = 0;
}

Selbst wenn dies der Fall TERMist xterm-mono, wird GNU grepbeschließen, Farben auszugeben, obwohl andere Programme dies vimnicht tun.

Der Win32-Port hat diesen Code , der die Einfärbung entweder dann aktiviert, wenn die TERMUmgebungsvariable nicht vorhanden ist oder wenn sie vorhanden ist und ihr Wert nicht mit dem festverdrahteten Namen übereinstimmt dumb:

int
should_colorize (nichtig)
{
  char const * t = getenv ("TERM");
  Rückkehr ! (t && strcmp (t, "dumm") == 0);
}

GNU grepProbleme mit Farbe

grepDie Färbung von GNU ist eigentlich berüchtigt. Da die Terminal-Ausgabe nicht ordnungsgemäß erstellt wird, sondern nur einige festverdrahtete Steuersequenzen an verschiedenen Stellen der Ausgabe ausgeführt werden, in der vergeblichen Hoffnung, dass dies ausreichend ist, wird unter bestimmten Umständen tatsächlich eine falsche Ausgabe angezeigt.

Unter diesen Umständen muss etwas am rechten Rand des Terminals eingefärbt werden. Programme, die Terminalausgaben korrekt ausführen, müssen automatische rechte Ränder berücksichtigen. Zusätzlich zu der geringen Möglichkeit, dass das Terminal möglicherweise nicht über sie verfügt (dh das auto_right_marginFeld in terminfo), folgt das Verhalten von Terminals, die über automatische rechte Ränder verfügen, häufig dem DEC VT-Präzedenzfall des Zeilenumbruchs . GNU grepberücksichtigt dies nicht und erwartet naiv einen sofortigen Zeilenumbruch , und seine farbige Ausgabe geht schief.

Farbausgabe ist keine einfache Sache.

Weitere Lektüre

JdeBP
quelle
2
Soweit ich weiß, fragt das OP nach der Änderung des Verhaltens, wenn die Ausgabe umgeleitet wird. $TERMerklärt das nicht. (Ihre Antwort ist im Allgemeinen interessant, aber ich glaube nicht, dass sie die Frage beantwortet ...)
Stephen Kitt,
sehr interessant. Ich wollte schon seit einigen Monaten einen Überblick darüber, wie Programme erkennen (oder einfach "entscheiden"), was ein Terminal kann. Dies gibt auch einen Einblick, warum es so schwer ist, eine Übersicht wie diese zu finden - weil jedes Programm es ein wenig anders zu machen scheint.
the_velour_fog
Eine Erklärung dessen, was ausstehender Zeilenumbruch und unmittelbarer Zeilenumbruch bedeuten, zusammen mit einem Beispiel, das den Unterschied demonstriert, wäre schön.
Mosvy
0

Der unbufferBefehl aus dem Expect- Paket entkoppelt die Ausgabe aus dem ersten Programm und die Eingabe in das zweite Programm.

Du würdest es so benutzen:

unbuffer myshellscript.sh | grep value

Ich benutze es die ganze Zeit mit Ansible und einem Homebrewed- Ctee- Skript, damit ich die Farbausgabe auf dem Terminal sehen kann, während ich die Protokolldatei mit normaler (nicht colorierter) Ausgabe lasse.

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