bash: drucke stderr in roter farbe

123

Gibt es eine Möglichkeit, Bash Stderr- Meldungen in roter Farbe anzuzeigen ?

kolypto
quelle
4
Ich vermute, dass Bash seine Ausgabe niemals einfärben wird: Einige Programme möchten möglicherweise etwas analysieren, und das Einfärben wird Daten mit maskierten Sequenzen verderben. Eine GUI-App sollte wohl mit Farben umgehen.
kolypto
Die Kombination von Balázs Pozsár und killdash9 ergibt das Knackige: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } Funktioniert für bash und zsh. Kann nicht als Antwort b / c Ruf hinzufügen.
Heinrich Hartmann
1
Ich warte auf eine Antwort, die Bash ändert, um dies zu tun. Die folgenden Lösungen modifizieren stderr tatsächlich und ordnen es möglicherweise sogar in der Reihenfolge stdout neu an, was zu Problemen führt, wenn die genaue Bytereihenfolge von stderr beibehalten werden muss, z. B. beim Piping.
Masterxilo

Antworten:

98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Balázs Pozsár
quelle
6
Toll! Aber ich frage mich, ob es eine Möglichkeit gibt, es dauerhaft zu machen :)
kolypto
9
Toller Tipp! Vorschlag: Durch Hinzufügen von >&2rechts zuvor ; done)wird die für stderr vorgesehene Ausgabe tatsächlich in stderr geschrieben. Dies ist hilfreich, wenn Sie die normale Ausgabe des Programms erfassen möchten.
Henko
8
Die folgenden Verwendungen tputund ist meiner Meinung nach etwas besser lesbar:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski
2
Ich denke, dass das Ausführen von 2 tput-Prozessen für jede Ausgabezeile überhaupt nicht elegant ist. Vielleicht würden Sie die Ausgabe der tput-Befehle in einer Variablen speichern und diese für jedes Echo verwenden. Andererseits ist die Lesbarkeit nicht wirklich besser.
Balázs Pozsár
1
Diese Lösung bewahrt keine Leerzeichen, aber ich mag sie wegen ihrer Kürze. IFS= read -r linesollte helfen, tut es aber nicht. Nicht sicher warum.
Max Murphy
89

Methode 1: Prozesssubstitution verwenden:

command 2> >(sed $'s,.*,\e[31m&\e[m,'>&2)

Methode 2: Erstellen Sie eine Funktion in einem Bash-Skript:

color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1

Benutze es so:

$ color command

Beide Methoden zeigen die Befehle stderrin rot an.

Lesen Sie weiter, um zu erfahren, wie Methode 2 funktioniert. Mit diesem Befehl werden einige interessante Funktionen demonstriert.

  • color()... - Erstellt eine Bash-Funktion namens color.
  • set -o pipefail- Dies ist eine Shell-Option, die den Fehlerrückgabecode eines Befehls beibehält, dessen Ausgabe an einen anderen Befehl weitergeleitet wird. Dies geschieht in einer Subshell, die durch die Klammern erstellt wird, um die Pipefail-Option in der äußeren Shell nicht zu ändern.
  • "$@"- Führt die Argumente der Funktion als neuen Befehl aus. "$@"ist äquivalent zu"$1" "$2" ...
  • 2>&1- Leitet den stderrBefehl von auf um, stdoutso dass er zu sed's wird stdin.
  • >&3- 1>&3Diese Abkürzung leitet stdoutzu einem neuen temporären Dateideskriptor weiter 3. 3wird stdoutspäter weitergeleitet.
  • sed ...- Aufgrund der oben genannten Weiterleitungen handelt es sich bei sed's stdinum stderrden ausgeführten Befehl. Seine Funktion besteht darin, jede Zeile mit Farbcodes zu umgeben.
  • $'...' Ein Bash-Konstrukt, mit dem Zeichen mit umgekehrten Schrägstrichen verstanden werden
  • .* - Entspricht der gesamten Zeile.
  • \e[31m - Die ANSI-Escape-Sequenz, bei der die folgenden Zeichen rot angezeigt werden
  • &- Das sedErsetzungszeichen, das auf die gesamte übereinstimmende Zeichenfolge (in diesem Fall die gesamte Zeile) erweitert wird.
  • \e[m - Die ANSI-Escape-Sequenz, mit der die Farbe zurückgesetzt wird.
  • >&2- Stenografie für 1>&2diese Umleitungen sed‚s stdoutan stderr.
  • 3>&1- Leitet den temporären Dateideskriptor 3zurück in stdout.
killdash9
quelle
9
+1 beste Antwort! absolut unterschätzt!
muhqu
3
Tolle Antwort und noch bessere Erklärung
Daniel Serodio
Warum müssen Sie die gesamte zusätzliche Umleitung durchführen? scheint wie Overkill
Qodeninja
1
Gibt es eine Möglichkeit, es zum Laufen zu bringen zsh?
Eyal Levin
1
ZSH erkennt die Kurzumleitungsformulare nicht. Es braucht nur noch zwei zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
Einsen
26

Sie können auch stderred auschecken: https://github.com/sickill/stderred

Sichel
quelle
Wow, dieses Dienstprogramm ist großartig. Das einzige, was es brauchen würde, ist ein passendes Repository, das es für alle Benutzer installiert, mit einer einzigen Zeile, ohne dass Sie mehr arbeiten müssen, um es zu aktivieren.
Sorin
Schien gut zu funktionieren, als ich es mit einem Build-Skript in einem separaten Terminal getestet habe, aber ich zögere, es global (in .bashrc) zu verwenden. Trotzdem danke!
Joel Purra
2
In OS X El Capitan ist die Funktionsweise (DYLD_INSERT_LIBRARIES) in Systembinärdateien "fehlerhaft", da sie durch SIP geschützt sind. Daher ist es möglicherweise besser, die in anderen Antworten angegebenen Bash-Optionen zu verwenden.
Hmijail
1
@hmijail für MacOS folgen Sie bitte github.com/sickill/stderred/issues/60, damit wir einen Workaround finden können. Ein Teil existiert bereits, ist aber etwas fehlerhaft.
Sorin
15

Die Bash-Methode, um stderr permanent rot zu machen, besteht darin, mit 'exec' Streams umzuleiten. Fügen Sie Ihrem bashrc Folgendes hinzu:

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "\033[31m${line}\033[0m"
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Ich habe bereits darüber geschrieben: So stellen Sie die Schriftfarbe für STDOUT und STDERR ein

Evangelien
quelle
1
Dies ist mit Abstand die beste Antwort. Einfache Implementierung ohne Installation / Sudo-Berechtigung. Kann auf alle Befehle verallgemeinert werden.
Luke Davis
1
Leider funktioniert dies nicht gut mit der Befehlsverkettung (Befehl && nextCommand || errorHandlerCommand). Die Fehlerausgabe erfolgt nach der Ausgabe von errorHandlerCommand.
carlin.scott
1
Ebenso, wenn ich source ~/.bashrczweimal damit bin, stürzt mein Terminal im Grunde ab.
Dolph
@Dolf: In meinem bashrc hüte ich mich leicht davor mit einer umgebenden if-Anweisung, um zu verhindern, dass dieser Code erneut geladen wird. Ansonsten liegt das Problem in der Umleitung 'exec 9> & 2', nachdem die Umleitung bereits stattgefunden hat. Ändern Sie es möglicherweise in eine Konstante, wenn Sie wissen, auf was> 2 ursprünglich zeigt.
Evangelien
7

Ich habe ein Wrapper-Skript erstellt, das die Antwort von Balázs Pozsár in pure bash umsetzt. Speichern Sie es in Ihren Befehlen $ PATH und prefix, um deren Ausgabe einzufärben.

    #! / bin / bash

    if [$ 1 == "--help"]; dann
        echo "Führt einen Befehl aus und färbt alle aufgetretenen Fehler ein."
        echo "Beispiel:` basename $ {0} `wget ..."
        Echo "(c) o_O Tync, ICQ # 1227-700, Viel Spaß!"
        Ausfahrt 0
        fi

    # Temp-Datei, um alle Fehler abzufangen
    TMP_ERRS = $ (mktemp)

    # Befehl ausführen
    "$ @" 2>> (während die Zeile gelesen wird; mache ein Echo -e "\ e [01; 31m $ line \ e [0m" | tee - füge $ TMP_ERRS hinzu; done)
    EXIT_CODE = $?

    # Alle Fehler erneut anzeigen
    if [-s "$ TMP_ERRS"]; dann
        echo -e "\ n \ n \ n \ e [01; 31m === FEHLER === \ e [0m"
        cat $ TMP_ERRS
        fi
    rm -f $ TMP_ERRS

    # Fertig
    Beenden Sie $ EXIT_CODE

kolypto
quelle
2
Dies könnte effizienter gestaltet werden, wenn "| tee ..." nach "done" gesetzt wird.
Juliano
3

Sie können eine solche Funktion verwenden


 #!/bin/sh

color() {
      printf '\033[%sm%s\033[m\n' "$@"
      # usage color "31;5" "string"
      # 0 default
      # 5 blink, 1 strong, 4 underlined
      # fg: 31 red,  32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
      # bg: 40 black, 41 red, 44 blue, 45 purple
      }
string="Hello world!"
color '31;1' "$string" >&2

Ich hänge> & 2 an, um nach stderr zu drucken

Ali Mezgani
quelle
4
Das Problem nicht ansprechen. Sie haben keine Möglichkeit zur Verfügung gestellt, stderr von stdout zu trennen, was das OP interessiert.
Jeremy Visser
1

Ich habe eine leicht modifizierte Version von O_o Tyncs Skript. Ich musste diese Mods für OS X Lion machen und es ist nicht perfekt, da das Skript manchmal abgeschlossen ist, bevor der umschlossene Befehl ausgeführt wird. Ich habe einen Schlaf hinzugefügt, aber ich bin mir sicher, dass es einen besseren Weg gibt.

#!/bin/bash

   if [ $1 == "--help" ] ; then
       echo "Executes a command and colorizes all errors occured"
       echo "Example: `basename ${0}` wget ..."
       echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!"
       exit 0
       fi

   # Temp file to catch all errors
   TMP_ERRS=`mktemp /tmp/temperr.XXXXXX` || exit 1

   # Execute command
   "$@" 2> >(while read line; do echo -e "$(tput setaf 1)$line\n" | tee -a $TMP_ERRS; done)
   EXIT_CODE=$?

   sleep 1
   # Display all errors again
   if [ -s "$TMP_ERRS" ] ; then
       echo -e "\n\n\n$(tput setaf 1) === ERRORS === "
       cat $TMP_ERRS
   else
       echo "No errors collected in $TMP_ERRS"
   fi
   rm -f $TMP_ERRS

   # Finish
   exit $EXIT_CODE
Cliff
quelle
1

Diese Lösung hat bei mir funktioniert: https://superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

Ich habe diese Funktion in meine .bashrcoder .zshrc:

# Red STDERR
# rse <command string>
function rse()
{
    # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace
    # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back
    ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/$(echo -en \\033)[31;1m\1$(echo -en \\033)[0m/") 3>&1 1>&2 2>&3
}

Dann zum Beispiel:

$ rse cat non_existing_file.txt

wird mir einen roten ausgang geben.

Eyal Levin
quelle
Sie können set -o pipefail;vor (evalfür die Umleitung Exit-Code hinzufügen
kvaps
"
Fügen Sie
1

mit xargs und printf:

command 2> >(xargs -0 printf "\e[31m%s\e[m" >&2)
Carlos Barcellos
quelle
0

eine version mit fifos

mkfifo errs
stdbuf -o0 -e0 -i0 grep . foo | while read line; do echo -e "\e[01;31m$line  \e[0m" >&2; done &
stdbuf -o0 -e0 -i0 sh $script 2>errs
untore
quelle