Kann ich meine Shell so konfigurieren, dass STDERR und STDOUT in verschiedenen Farben gedruckt werden?

63

Ich möchte mein Terminal so einrichten, dass stderres in einer anderen Farbe gedruckt wird als stdout; vielleicht rot. Dies würde es einfacher machen, die beiden voneinander zu unterscheiden.

Gibt es eine Möglichkeit, dies zu konfigurieren .bashrc? Wenn nicht, ist das überhaupt möglich?


Hinweis : Diese Frage wurde mit einer anderen Frage zusammengeführt stderr, stdout und das Benutzereingabe-Echo wurde in 3 verschiedenen Farben ausgegeben . Die Antworten können sich auf beide Fragen beziehen.

Naftuli Kay
quelle
1
Dieselbe
Stéphane Gimenez
Interessante Frage + Antwort, aber rot steht zu viel IMO, da stderr nicht nur für Fehler ist
krookedking

Antworten:

32

Dies ist eine härtere Version von Show only stderr on screen, aber schreibe sowohl stdout als auch stderr in die Datei .

Die auf dem Terminal ausgeführten Anwendungen verwenden einen einzelnen Kanal, um mit ihm zu kommunizieren. Die Anwendungen haben zwei Ausgangsports, stdout und stderr, aber beide sind mit demselben Kanal verbunden.

Sie können einen davon mit einem anderen Kanal verbinden, diesem Kanal Farbe hinzufügen und die beiden Kanäle zusammenführen. Dies führt jedoch zu zwei Problemen:

  • Die zusammengeführte Ausgabe weist möglicherweise nicht genau die gleiche Reihenfolge auf, als hätte es keine Umleitung gegeben. Dies liegt daran, dass die hinzugefügte Verarbeitung in einem der Kanäle (etwas) Zeit in Anspruch nimmt, sodass der farbige Kanal möglicherweise verzögert wird. Wenn eine Pufferung durchgeführt wird, wird die Störung schlimmer sein.
  • Terminals verwenden farbwechselnde Escape-Sequenzen, um die Anzeigefarbe zu bestimmen, z. B. ␛[31m"auf roten Vordergrund umschalten". Dies bedeutet, dass die Ausgabe verfärbt wird, wenn eine Ausgabe für stdout eingeht, während eine Ausgabe für stderr angezeigt wird. (Schlimmer noch, wenn sich mitten in einer Escape-Sequenz ein Kanalwechsel befindet, sehen Sie Müll.)

Im Prinzip wäre es möglich, ein Programm zu schreiben, das synchron auf zwei ptys¹ lauscht (dh Eingaben auf einem Kanal werden nicht akzeptiert, während Ausgaben auf dem anderen Kanal verarbeitet werden) und sofort mit entsprechenden Anweisungen zum Farbwechsel an das Terminal auszugeben. Sie verlieren die Möglichkeit, Programme auszuführen, die mit dem Terminal interagieren. Ich kenne keine Implementierung dieser Methode.

Ein anderer möglicher Ansatz wäre, das Programm zu veranlassen, die richtigen Farbänderungssequenzen auszugeben, indem alle libc-Funktionen, die den writeSystemaufruf aufrufen, in einer mit geladenen Bibliothek eingebunden werden LD_PRELOAD. Sehen Sie sich die Antwort von sickill für eine vorhandene Implementierung oder die Antwort von Stéphane Chazelas für einen gemischten Ansatz an, der die Vorteile nutzt strace.

In der Praxis empfehle ich, wenn dies zutrifft, stderr auf stdout umzuleiten und in einen musterbasierten Colorizer wie Colortail oder Multitail oder spezielle Colorizer wie Colorgcc oder Colormake zu leiten .

¹ Pseudo-Terminals. Pipes würden aufgrund von Pufferung nicht funktionieren: Die Quelle könnte in den Puffer schreiben, was die Synchronität mit dem Colorizer beeinträchtigen würde.

Gilles 'SO - hör auf böse zu sein'
quelle
1
Es ist möglicherweise nicht schwierig, ein Terminalprogramm zu patchen, um den stderr-Stream einzufärben. Jemand hat so etwas bei Ubuntu Brainstorm vorgeschlagen .
Intuited
@intuited: Dazu müsste jeder Terminal-Emulator, mit dem dies funktionieren soll, nachverfolgt werden. Das Verwenden von LD_PRELOADTricks zum Abfangen von writeAnrufen scheint am besten geeignet zu sein, IMO (aber es kann auch Unterschiede bei bestimmten * nix-Varianten geben.)
alex
Zumindest unter Linux writewürde das Abfangen alleine nicht funktionieren, da die meisten Anwendungen nicht direkt aufrufen, sondern eine andere Funktion aus einer gemeinsam genutzten Bibliothek (wie printf), die das Original aufrufen würdewrite
Stéphane Chazelas,
@StephaneChazelas Ich dachte daran, mich um den writeSyscall-Wrapper zu kümmern . Ist es in anderen Funktionen in Glibc eingebettet?
Gilles 'SO- hör auf, böse zu sein'
1
Das stderred-Projekt scheint eine Implementierung von Hooking writevia zu sein, LD_PRELOADwie Sie beschreiben.
Drew Noakes
36

Auschecken stderred. Es verwendet , LD_PRELOADum hook libc‚s write()Anrufe Einfärben alle stderrAusgänge gehen an einen Terminal. (Standardmäßig in Rot.)

Sichel
quelle
8
Schön, dass die Bibliothek großartig ist . Die eigentliche Frage ist: Warum wird mein Betriebssystem / Terminal mit dieser vorinstallierten Version nicht geliefert? ;)
Naftuli Kay
5
Ich nehme an, Sie sind der Autor, stimmt das? Sie sollten in diesem Fall Ihre Zugehörigkeit offenlegen.
Dmitry Grigoryev
15

Das Färben von Benutzereingaben ist schwierig, da sie in der Hälfte der Fälle vom Terminaltreiber (mit lokalem Echo) ausgegeben werden. In diesem Fall weiß möglicherweise keine Anwendung, die in diesem Terminal ausgeführt wird, wann der Benutzer Text eingeben und die Ausgabefarbe entsprechend ändern wird . Nur der Pseudo-Terminal-Treiber (im Kernel) weiß, dass der Terminal-Emulator (wie xterm) bei einem Tastendruck einige Zeichen sendet, und der Terminal-Treiber sendet möglicherweise einige Zeichen für das Echo zurück, aber xterm kann nicht wissen, ob diese vom stammen lokales Echo oder was die Anwendung an die Slave-Seite des Pseudoterminals ausgibt).

Und dann gibt es noch den anderen Modus, in dem der Terminal-Treiber angewiesen wird, nicht zu echo, sondern die Anwendung diesmal etwas ausgibt. Die Anwendung (wie die, die readline wie gdb, bash ... verwenden) sendet möglicherweise das auf stdout oder stderr, was schwierig von etwas zu unterscheiden ist, das sie für andere Zwecke ausgibt, als die Benutzereingabe zurückzugeben.

Es gibt verschiedene Ansätze, um den Standardwert einer Anwendung von ihrem Standardwert zu unterscheiden.

Bei vielen von ihnen werden die Befehle stdout und stderr an Pipes umgeleitet, und diese Pipes werden von einer Anwendung gelesen, um sie einzufärben. Damit sind zwei Probleme verbunden:

  • Sobald stdout kein Terminal mehr ist (wie eine Pipe), passen viele Anwendungen ihr Verhalten an, um die Ausgabe zu puffern. Dies bedeutet, dass die Ausgabe in großen Blöcken angezeigt wird.
  • Auch wenn es derselbe Prozess ist, der die beiden Pipes verarbeitet, gibt es keine Garantie, dass die Reihenfolge, in der der von der Anwendung auf stdout und stderr geschriebene Text gespeichert wird, beibehalten wird, da der Lesevorgang nicht wissen kann (ob von beiden etwas zu lesen ist). ob mit dem Lesen von der "stdout" -Pipe oder der "stderr" -Pipe begonnen werden soll.

Ein weiterer Ansatz besteht darin, die Anwendung so zu ändern, dass sie ihre Standard- und Standardfarben einfärbt. Dies ist oft nicht möglich oder realistisch.

Dann kann ein Trick (für dynamisch verknüpfte Anwendungen) darin bestehen, die von der Anwendung aufgerufenen Ausgabefunktionen zu missbrauchen ( $LD_PRELOADwie in der Antwort von sickill ), um etwas auszugeben, und darin Code aufzunehmen, der die Vordergrundfarbe basierend darauf festlegt, ob sie etwas ausgeben sollen auf stderr oder stdout. Dies bedeutet jedoch, dass jede mögliche Funktion aus der C-Bibliothek und jeder anderen Bibliothek entführt wird, die einen write(2)Systemaufruf ausführt, der direkt von der Anwendung aufgerufen wird und möglicherweise etwas auf stdout oder stderr (printf, puts, perror ...) schreibt, und sogar dann kann sein Verhalten ändern.

Ein anderer Ansatz könnte darin bestehen, PTRACE-Tricks bei jedem Aufruf des Systemaufrufs zu verwenden straceoder gdbsich selbst zu verbinden write(2)und die Ausgabefarbe basierend darauf festzulegen, ob sich der write(2)in Dateideskriptor 1 oder 2 befindet.

Das ist jedoch eine ziemlich große Sache.

Ein Trick, mit dem ich gerade gespielt habe, ist, stracesich mit LD_PRELOAD selbst zu hijacken (was die Drecksarbeit macht, sich vor jedem Systemaufruf einzuhängen), um ihm mitzuteilen, dass er die Ausgabefarbe ändern soll, je nachdem, ob er write(2)auf fd 1 oder a erkannt hat 2.

Wenn stracewir uns den Quellcode ansehen, können wir sehen, dass alles, was er ausgibt, über die vfprintfFunktion erfolgt. Alles was wir tun müssen, ist diese Funktion zu hijacken.

Der LD_PRELOAD-Wrapper würde folgendermaßen aussehen:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>

int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
  static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
  static int c = 0;
  va_list ap_orig;
  va_copy(ap_orig, ap);
  if (!orig_vfprintf) {
    orig_vfprintf = (int (*) (FILE*, const char *, va_list))
      dlsym (RTLD_NEXT, "vfprintf");
  }

  if (strcmp(fmt, "%ld, ") == 0) {
    int fd = va_arg(ap, long);
    switch (fd) {
    case 2:
      write(2, "\e[31m", 5);
      c = 1;
      break;
    case 1:
      write(2, "\e[32m", 5);
      c = 1;
      break;
    }
  } else if (strcmp(fmt, ") ") == 0) {
    if (c) write(2, "\e[m", 3);
    c = 0;
  }
  return orig_vfprintf(outf, fmt, ap_orig);
}

Dann kompilieren wir es mit:

cc -Wall -fpic -shared -o wrap.so wrap.c -ldl

Und benutze es als:

LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd

Sie werden feststellen, dass beim Ersetzen some-cmddurch bashdie Bash-Eingabeaufforderung und die zshEingabe in Rot (stderr) angezeigt wird, während sie in Schwarz angezeigt wird (da zsh stderr auf einen neuen fd kopiert, um die Eingabeaufforderung und das Echo anzuzeigen).

Es scheint überraschend gut zu funktionieren, auch für Anwendungen, die Sie nicht erwarten würden (wie diejenigen, die Farben verwenden).

Der Farbmodus wird auf stracedem stderr ausgegeben , der als Terminal angenommen wird. Wenn die Anwendung ihre stdout oder stderr umleitet, schreibt unsere entführte Strace die farbigen Escape-Sequenzen weiterhin auf das Terminal.

Diese Lösung hat ihre Grenzen:

  • Jene, die inhärent sind mit strace: Leistungsproblemen, Sie können keine anderen PTRACE-Befehle wie straceoder gdbdarin oder Setuid / Setgid-Probleme ausführen
  • Die Färbung basiert auf den writeStandard- / Standardfarben jedes einzelnen Prozesses. So zum Beispiel in sh -c 'echo error >&2', errorwäre grün , weil echogibt sie auf seine stdout (die sh des stderr umgeleitet sh, aber alle Strace sieht ein write(1, "error\n", 6)). Und in sh -c 'seq 1000000 | wc', seqhat viel oder wenig writemit seiner Standardausgabe zu tun, sodass der Wrapper am Ende viele (unsichtbare) Escape-Sequenzen auf dem Terminal ausgibt.
Stéphane Chazelas
quelle
Nett. Es gab Vorschläge von bereits existierenden Wrappern zu der doppelten Frage . Ich habe die Frage zum Zusammenführen markiert, damit Ihre Antwort dort angezeigt wird.
Gilles 'SO- hör auf, böse zu sein'
Vielleicht vim Syntax Highlight zwicken? strace $CMD | vim -c ':set syntax=strace' -.
Pablo A
4

Hier ist ein Proof of Concept, den ich vor einiger Zeit gemacht habe.

Es funktioniert nur in zsh.

# make standard error red
rederr()
{
    while read -r line
    do
        setcolor $errorcolor
        echo "$line"
        setcolor normal
    done
}

errorcolor=red

errfifo=${TMPDIR:-/tmp}/errfifo.$$
mkfifo $errfifo
# to silence the line telling us what job number the background job is
exec 2>/dev/null
rederr <$errfifo&
errpid=$!
disown %+
exec 2>$errfifo

Es wird auch davon ausgegangen, dass Sie eine Funktion namens setcolor haben.

Eine vereinfachte Version:

setcolor()
{
    case "$1" in
    red)
        tput setaf 1
        ;;
    normal)
        tput sgr0
        ;;
    esac
}
Mikel
quelle
Es gibt eine viel einfachere Art und Weise , dies zu tun: exec 2> >(rederr). In beiden Versionen wird es Probleme geben, die ich in meiner Antwort erwähne, nämlich die Reihenfolge der Zeilen zu ändern und die Ausgabe zu gefährden (insbesondere bei langen Zeilen).
Gilles 'SO- hör auf böse zu sein'
Ich habe es versucht und es hat nicht funktioniert.
Mikel
seterrmüsste ein eigenständiges Skript sein, keine Funktion.
Gilles 'SO- hör auf böse zu sein'
4

Siehe Mike Schiraldis Hilite, der dies jeweils für einen Befehl ausführt . Mein eigener Schwall tut dies für eine ganze Sitzung, hat aber auch viele andere Funktionen / Eigenheiten, die Sie vielleicht nicht wollen.

Colin Macleod
quelle