bash: drukuj stderr w kolorze czerwonym

122

Czy istnieje sposób, aby wyświetlać komunikaty stderr bash w kolorze czerwonym?

kolypto
źródło
4
Wydaje mi się, że bash nigdy nie pokoloruje danych wyjściowych: jakiś program może chcieć coś przeanalizować, a kolorowanie zepsuje dane za pomocą sekwencji specjalnych. Chyba aplikacja GUI powinna obsługiwać kolory.
kolypto
Połączenie odpowiedzi Balázsa Pozsára i killdash9 daje ostry: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } działa na bash i zsh. Nie można tego dodać jako reputacji b / c.
Heinrich Hartmann
1
Czekam na odpowiedź, która modyfikuje bash, aby to zrobić. Wszystkie poniższe rozwiązania faktycznie modyfikują stderr, a być może nawet zmieniają jego kolejność na standardowe, co psuje rzeczy, gdy zachowana musi być dokładna sekwencja bajtów stderr, np. Podczas instalacji.
masterxilo,

Odpowiedzi:

98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Balázs Pozsár
źródło
6
Wspaniały! Ale zastanawiam się, czy jest jakiś sposób, aby to zrobić na stałe :)
kolypto
9
Świetna wskazówka! Sugestia: Dodając już >&2wcześniej ; done), wyjście przeznaczone dla stderr faktycznie jest zapisywane do stderr. Jest to pomocne, jeśli chcesz uchwycić normalne wyjście programu.
henko
8
Następujące zastosowania tput, i moim zdaniem, są nieco bardziej czytelne:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski
2
Myślę, że wykonanie 2 procesów tput dla każdej linii wyjściowej wcale nie jest eleganckie. Może gdybyś zapisał dane wyjściowe poleceń tput w zmiennej i używałby ich dla każdego echa. Ale z drugiej strony czytelność nie jest tak naprawdę lepsza.
Balázs Pozsár
1
To rozwiązanie nie zachowuje białych znaków, ale lubię je za zwięzłość. IFS= read -r linepowinien pomóc, ale nie pomaga. Nie pewny dlaczego.
Max Murphy,
89

Metoda 1: Użyj podstawienia procesu:

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

Metoda 2: Utwórz funkcję w skrypcie bash:

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

Użyj tego w ten sposób:

$ color command

Obie metody pokażą komendę na stderrczerwono.

Czytaj dalej, aby wyjaśnić, jak działa metoda 2. Polecenie to pokazuje kilka interesujących funkcji.

  • color()... - Tworzy funkcję bash o nazwie kolor.
  • set -o pipefail- To jest opcja powłoki, która zachowuje kod powrotu błędu polecenia, którego dane wyjściowe są przesyłane do innego polecenia. Odbywa się to w podpowłoce, która jest tworzona w nawiasach, aby nie zmieniać opcji pipefail w zewnętrznej powłoce.
  • "$@"- Wykonuje argumenty funkcji jako nowe polecenie. "$@"jest równa"$1" "$2" ...
  • 2>&1- Przekierowuje stderrpolecenie, aby stdoutstało się sed's stdin.
  • >&3- Skrótowo oznacza 1>&3to przekierowanie stdoutdo nowego tymczasowego deskryptora pliku 3. 3wraca stdoutpóźniej.
  • sed ...- Z powodu powyższych przekierowań, sed„s stdinjest stderrod wykonywanego polecenia. Jego funkcją jest otaczanie każdej linii kodami kolorów.
  • $'...' Konstrukcja bash, która powoduje, że rozumie znaki ucieczki odwrotnego ukośnika
  • .* - Pasuje do całej linii.
  • \e[31m - Sekwencja zmiany znaczenia ANSI, która powoduje, że następujące znaki są czerwone
  • &- Znak sedzastępujący, który rozwija się do całego dopasowanego łańcucha (w tym przypadku cała linia).
  • \e[m - Sekwencja ucieczki ANSI, która resetuje kolor.
  • >&2- skrótem 1>&2, to przekierowuje sed„s stdoutdo stderr.
  • 3>&1- Przekierowuje deskryptor pliku tymczasowego z 3powrotem do stdout.
killdash9
źródło
9
+1 najlepsza odpowiedź! absolutnie niedoceniany!
muhqu
3
Świetna odpowiedź i jeszcze lepsze wyjaśnienie
Daniel Serodio
Dlaczego musisz wykonać wszystkie dodatkowe przekierowania? wydaje się jak przesada
qodeninja
1
Czy istnieje sposób, aby to zadziałało zsh?
Eyal Levin,
1
ZSH nie rozpoznaje skrótowych form przekierowania. Potrzebuje tylko dwóch kolejnych 1, tj .:zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
Rekin
26

Możesz także sprawdzić stderred: https://github.com/sickill/stderred

chory
źródło
Wow, to narzędzie jest świetne, jedyne, czego potrzebuje, to mieć apt repozytorium, które instaluje je dla wszystkich użytkowników, w jednym wierszu, bez konieczności wykonywania dodatkowej pracy, aby je włączyć.
sorin,
Wydawało się, że działa dobrze, gdy przetestowałem go za pomocą skryptu kompilacji w osobnym terminalu, ale waham się używać go globalnie (w .bashrc). W każdym razie dzięki!
Joel Purra
2
W OS X El Capitan sposób, w jaki to działa (DYLD_INSERT_LIBRARIES) jest „uszkodzony” w binariach systemowych, ponieważ są chronione przez SIP. Dlatego może być lepiej użyć opcji bash podanych w innych odpowiedziach.
hmijail 22.04.16
1
@hmijail dla MacOS postępuj zgodnie z github.com/sickill/stderred/issues/60 , abyśmy mogli znaleźć obejście, częściowe już istnieje, ale jest trochę wadliwe.
sorin
15

Podstawowym sposobem, aby stderr był trwale czerwony, jest użycie „exec” do przekierowania strumieni. Dodaj następujące elementy do swojego 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;'

Wcześniej napisałem na ten temat: Jak ustawić kolor czcionki dla STDOUT i STDERR

ewangelie
źródło
1
Jak dotąd najlepsza odpowiedź; łatwa do wdrożenia bez instalacji / wymagająca uprawnień sudo i może być uogólniona na wszystkie polecenia.
Luke Davis,
1
Niestety nie działa to dobrze z łańcuchem poleceń (Command && nextCommand || errorHandlerCommand). Wyjście błędu następuje po wyjściu errorHandlerCommand.
carlin.scott
1
Podobnie, jeśli zrobię to source ~/.bashrcdwa razy, mój terminal zasadniczo się zablokuje.
Dolph
@Dolf: W moim bashrc z łatwością chronię się przed tym za pomocą instrukcji otaczającej if, aby zapobiec przeładowaniu tego kodu. W przeciwnym razie problemem jest przekierowanie „exec 9> i 2” po tym, jak przekierowanie już miało miejsce. Być może zmień go na stałą, jeśli wiesz, gdzie> 2 wskazuje pierwotnie.
gospes
7

Zrobiłem skrypt otoki, który implementuje odpowiedź Balázsa Pozsár w czystym bashu. Zapisz go w komendach $ PATH i prefiksach, aby pokolorować ich dane wyjściowe.

    #! / bin / bash

    if [$ 1 == "--help"]; następnie
        echo „Wykonuje polecenie i koloruje wszystkie napotkane błędy”
        echo "Przykład:` basename $ {0} `wget ..."
        echo "(c) o_O Tync, ICQ # 1227-700, Enjoy!"
        wyjście 0
        fi

    # Plik tymczasowy, aby przechwycić wszystkie błędy
    TMP_ERRS = $ (mktemp)

    # Wykonaj polecenie
    "$ @" 2>> (podczas odczytu linii; wykonaj echo -e "\ e [01; 31m $ line \ e [0m" | tee - dołącz $ TMP_ERRS; gotowe)
    EXIT_CODE = $?

    # Wyświetl ponownie wszystkie błędy
    jeśli [-s „$ TMP_ERRS”]; następnie
        echo -e "\ n \ n \ n \ e [01; 31m === BŁĘDY === \ e [0m”
        kot $ TMP_ERRS
        fi
    rm -f $ TMP_ERRS

    # Koniec
    wyjdź z $ EXIT_CODE

kolypto
źródło
2
Można by to uczynić bardziej wydajnym, gdyby wstawiono „| tee ...” po zakończeniu.
Juliano
3

Możesz użyć takiej funkcji


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

Dołączam> i 2, aby wydrukować na stderr

Ali Mezgani
źródło
4
Nie rozwiązuje problemu. Nie podałeś sposobu na oddzielenie stderr od stdout, czym interesuje się OP.
Jeremy Visser
1

Mam nieco zmodyfikowaną wersję skryptu O_o Tync. Musiałem zrobić te mody dla OS X Lion i nie jest to idealne, ponieważ skrypt czasami kończy się, zanim zrobi to zapakowane polecenie. Dodałem sen, ale jestem pewien, że jest lepszy sposób.

#!/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
Klif
źródło
1

To rozwiązanie działało dla mnie: https://superuser.com/questions/28869/immed natychmiast-tell-which-output-was-sent-to-stderr

Umieściłem tę funkcję w moim .bashrclub .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
}

Następnie na przykład:

$ rse cat non_existing_file.txt

da mi czerwony wynik.

Eyal Levin
źródło
Możesz dodać set -o pipefail;wcześniej (evalkod wyjścia przekierowania
kvaps
dodaj także "do eval, aby zachować spacje w argumentach
kvaps
1

używając xargs i printf:

command 2> >(xargs -0 printf "\e[31m%s\e[m" >&2)
Carlos Barcellos
źródło
0

wersja korzystająca z 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
odtąd
źródło