bash: stampa stderr di colore rosso


123

C'è un modo per fare in modo che bash mostri i messaggi stderr in rosso?


4
Immagino che bash non colorerà mai il suo output: alcuni programmi potrebbero voler analizzare qualcosa, e colorizing rovinerà i dati con sequenze di escape. Un'app GUI dovrebbe gestire i colori, immagino.
Kolypto,

Combinando Balázs Pozsár e la risposta killdash9 si ottiene la risposta nitida: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } funziona con bash e zsh. Non è possibile aggiungere questo come risposta reputazione b / c.
Heinrich Hartmann,

1
Sto aspettando una risposta che modifichi bash per farlo. Le soluzioni sottostanti in realtà modificano effettivamente stderr e possibilmente riordinano anche stdout che rompe le cose quando l'esatta sequenza di byte di stderr deve essere preservata, ad esempio durante il piping.
masterxilo,

Risposte:


98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)

6
Grande! Ma mi chiedo se c'è un modo per renderlo permanente :)
kolypto il

9
Ottimo consiglio! Suggerimento: aggiungendo >&2subito prima ; done), l'output destinato a stderr viene effettivamente scritto su stderr. È utile se vuoi catturare l'output normale del programma.
henko,

8
I seguenti usi tput, ed è leggermente più leggibile secondo me:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski,

2
Penso che eseguire 2 processi di tput per ogni linea di output non sia affatto elegante. Forse se dovessi memorizzare l'output dei comandi tput in una variabile e usarli per ogni eco. Ma poi di nuovo, la leggibilità non è davvero migliore.
Balázs Pozsár,

1
Questa soluzione non preserva gli spazi bianchi ma mi piace per la sua brevità. IFS= read -r linedovrebbe aiutare ma non lo fa. Non so perché.
Max Murphy,

89

Metodo 1: utilizzare la sostituzione del processo:

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

Metodo 2: creare una funzione in uno script bash:

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

Usalo in questo modo:

$ color command

Entrambi i metodi mostreranno i comandi stderrin rosso.

Continua a leggere per una spiegazione di come funziona il metodo 2. Ci sono alcune caratteristiche interessanti dimostrate da questo comando.

  • color()... - Crea una funzione bash chiamata color.
  • set -o pipefail- Questa è un'opzione shell che preserva il codice di ritorno dell'errore di un comando il cui output viene reindirizzato in un altro comando. Questo viene fatto in una subshell, creata dalle parentesi, in modo da non modificare l'opzione pipefail nella shell esterna.
  • "$@"- Esegue gli argomenti per la funzione come nuovo comando. "$@"è equivalente a"$1" "$2" ...
  • 2>&1- Reindirizza il stderrcomando in stdoutmodo che diventi sed's stdin.
  • >&3- Abbreviazione di 1>&3, reindirizza stdouta un nuovo descrittore di file temporaneo 3. 3viene reindirizzato in un stdoutsecondo momento.
  • sed ...- A causa dei reindirizzamenti sopra, sed's stdinè il stderrcomando eseguito. La sua funzione è di circondare ogni riga con codici colore.
  • $'...' Un costrutto bash che gli fa capire i caratteri con escape backslash
  • .* - Corrisponde all'intera riga.
  • \e[31m - La sequenza di escape ANSI che fa diventare rossi i seguenti caratteri
  • &- Il sedcarattere di sostituzione che si espande all'intera stringa corrispondente (l'intera riga in questo caso).
  • \e[m - La sequenza di escape ANSI che reimposta il colore.
  • >&2- Stenografia per 1>&2, questo reindirizza sed's stdouta stderr.
  • 3>&1- Reindirizza 3nuovamente il descrittore di file temporaneo in stdout.

9
+1 migliore risposta! assolutamente sottovalutato!
muhqu,

3
Ottima risposta e spiegazione ancora migliore
Daniel Serodio,

Perché è necessario eseguire tutto il reindirizzamento aggiuntivo? sembra eccessivo
qodeninja

1
C'è un modo per farlo funzionare zsh?
Eyal Levin,

1
ZSH non riconosce i moduli di reindirizzamento stenografico. Ha solo bisogno di altri due 1, cioè:zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
Rekin il

26

Puoi anche dare un'occhiata a stderred: https://github.com/sickill/stderred


Caspita, questa utility è fantastica, l'unica cosa di cui avrebbe bisogno è avere un repository apt che lo installi per tutti gli utenti, con una sola riga, senza dover fare più lavoro per abilitarlo.
sorin,

Sembrava funzionare bene quando l'ho testato con uno script di build in un terminale separato, ma sono titubante nell'usarlo globalmente (in .bashrc). Grazie comunque!
Joel Purra,

2
In OS X El Capitan, il modo in cui funziona (DYLD_INSERT_LIBRARIES) è "interrotto" nei file binari di sistema perché sono protetti da SIP. Quindi potrebbe essere meglio usare le opzioni bash fornite in altre risposte.
Hmijail,

1
@hmijail per MacOS, segui github.com/sickill/stderred/issues/60 in modo da poter trovare una soluzione alternativa, già esiste una parziale ma è un po 'buggy.
sorin,

15

Il modo bash di rendere permanentemente rosso stderr sta usando 'exec' per reindirizzare i flussi. Aggiungi quanto segue al tuo bashrc:

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;'

Ho precedentemente pubblicato su questo: Come impostare il colore del carattere per STDOUT e STDERR



1
Questa è di gran lunga la risposta migliore; facile da implementare senza installazione / che richiede il privilegio sudo e può essere generalizzato a tutti i comandi.
Luke Davis,

1
Sfortunatamente questo non funziona bene con il concatenamento dei comandi (comando && nextCommand || errorHandlerCommand). L'output dell'errore segue l'output errorHandlerCommand.
carlin.scott,

1
Allo stesso modo, se source ~/.bashrcdue volte con questo, il mio terminale sostanzialmente si blocca.
Dolph,

@Dolf: Nella mia bashc mi difendo facilmente da questo con un'istruzione if circostante per evitare che questo codice venga ricaricato. Altrimenti, il problema è il reindirizzamento 'exec 9> & 2' dopo che il reindirizzamento è già avvenuto. Forse cambiarlo in una costante se sai dove> 2 punta originariamente.
Gospes,


7

Ho realizzato una sceneggiatura avvolgente che implementa la risposta di Balázs Pozsár in modo puro. Salvalo nei comandi $ PATH e prefisso per colorarne l'output.

    #! / Bin / bash

    if [$ 1 == "--help"]; poi
        echo "Esegue un comando e colora tutti gli errori occorsi"
        echo "Esempio:` basename $ {0} `wget ..."
        echo "(c) o_O Tync, ICQ # 1227-700, divertiti!"
        uscita 0
        fi

    # File temporaneo per rilevare tutti gli errori
    TMP_ERRS = $ (mktemp)

    # Esegui comando
    "$ @" 2>> (mentre leggi la riga; do echo -e "\ e [01; 31m $ line \ e [0m" | tee --append $ TMP_ERRS; done)
    EXIT_CODE = $?

    # Visualizza di nuovo tutti gli errori
    if [-s "$ TMP_ERRS"]; poi
        echo -e "\ n \ n \ n \ e [01; 31m === ERRORI === \ e [0m"
        cat $ TMP_ERRS
        fi
    rm -f $ TMP_ERRS

    # Finire
    uscita $ EXIT_CODE


2
Ciò potrebbe essere reso più efficiente se "| tee ..." fosse inserito dopo "completato".
Juliano,

3

Puoi usare una funzione come questa


 #!/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

Aggiungo> & 2 per stampare su stderr


4
Non affrontare il problema. Non hai fornito un modo per separare stderr da stdout, che è ciò che interessa all'OP.
Jeremy Visser,

1

Ho una versione leggermente modificata della sceneggiatura di O_o Tync. Avevo bisogno di creare queste mod per OS X Lion e non è perfetto perché lo script a volte si completa prima del comando wrapping. Ho aggiunto un sonno ma sono sicuro che c'è un modo migliore.

#!/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

1

Questa soluzione ha funzionato per me: https://superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

Ho inserito questa funzione nel mio .bashrco .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
}

Quindi ad esempio:

$ rse cat non_existing_file.txt

mi darà un risultato rosso.


Puoi aggiungere set -o pipefail;prima (evalper il codice di uscita di reindirizzamento
kvaps il

aggiungi anche il "to eval per preservare gli spazi negli argomenti
kvaps


0

una versione che utilizza 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.