bash: drukuj stderr w kolorze czerwonym


122

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


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)

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.

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


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



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


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


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

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.


Możesz dodać set -o pipefail;wcześniej (evalkod wyjścia przekierowania
— kvaps

dodaj także "do eval, aby zachować spacje w argumentach
— kvaps


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.