web-dev-qa-db-fra.com

Comment définir la couleur de police pour STDOUT et STDERR

Je souhaite différencier les messages STDOUT et STDERR dans mon terminal. Si un script ou une commande imprime un message dans le terminal, je souhaite différencier par des couleurs; c'est possible?

(Par exemple, la couleur de la police stderr est le rouge et la couleur de la police stdout est le bleu.)

Exemple (en gras):

$date
Wed Jul 27 12:36:50 IST 2011

$datee
bash: datee: command not found

$alias ls
alias ls='ls --color=auto -F'

$aliass ls
bash: aliass: command not found

34
ungalnanban

Voici un hack auquel j'ai pensé et qui semble fonctionner:

Étant donné les alias suivants pour la lisibilité:

alias blue='echo -en "\033[36m"'
alias red='echo -en "\033[31m"'
alias formatOutput='while read line; do blue; echo $line; red; done'

Maintenant, vous devez d'abord définir la couleur de police de votre terminal sur rouge (comme valeur par défaut, elle sera utilisée pour stderr). Ensuite, exécutez votre commande et dirigez la sortie standard vers formatOutput défini chaque ligne en bleu puis réinitialise la couleur de la police en rouge):

Shell$ red
Shell$ ls / somenonexistingfile | formatOutput

La commande ci-dessus imprimera à la fois dans stderr et stdout et vous verrez que les lignes sont colorées différemment.

J'espère que cela t'aides


METTRE À JOUR:

Pour que cela soit réutilisable, j'ai tout écrit dans un petit script:

$ cat bin/run 
#!/bin/bash
echo -en "\033[31m"  ## red
eval $* | while read line; do
    echo -en "\033[36m"  ## blue
    echo $line
    echo -en "\033[31m"  ## red
done
echo -en "\033[0m"  ## reset color

Maintenant, vous pouvez l'utiliser avec n'importe quelle commande:

$ run yourCommand
22
Costi Ciudatu

Créez une fonction dans un shell ou un script bash:

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

Utilisez-le comme ceci:

$ color command -program -args

La commande affiche stderren rouge. 

Continuez votre lecture pour une explication de la façon dont cela fonctionne. Certaines fonctions intéressantes sont illustrées par cette commande.

  • color()... - Crée une fonction bash appelée couleur.
  • set -o pipefail - Ceci est une option du shell qui conserve le code de retour d'erreur d'une commande dont la sortie est acheminée dans une autre commande. Ceci est fait dans un sous-shell, qui est créé par les parenthèses, afin de ne pas changer l'option pipefail dans le shell externe. 
  • "$@" - Exécute les arguments de la fonction en tant que nouvelle commande. "$@" est équivalent à "$1" "$2" ...
  • 2>&1 - Redirige le stderrde la commande en stdoutafin qu'il devienne sedname __ 's stdinname__.
  • >&3 - Abrégé pour 1>&3, ceci redirige stdoutvers un nouveau descripteur de fichier temporaire 3. 3 est redirigé dans stdoutplus tard.
  • sed ... - En raison des redirections ci-dessus, sedname __ 's stdinest le stderrde la commande exécutée. Sa fonction est d’entourer chaque ligne de codes de couleur.
  • $'...' Une construction bash qui lui permet de comprendre les caractères échappés par une barre oblique inverse
  • .* - Correspond à la ligne entière.
  • \e[31m - La séquence d'échappement ANSI qui fait apparaître les caractères suivants en rouge
  • & - Le caractère sedqui remplace la chaîne correspondante (la ligne entière dans ce cas).
  • \e[m - La séquence d'échappement ANSI qui réinitialise la couleur.
  • >&2 - Abrégé pour 1>&2, ceci redirige sedname __ 's stdouten stderrname__.
  • 3>&1 - Réoriente le descripteur de fichier temporaire 3 dans stdoutname__.
47
killdash9

Je couleur stderr red en liant le descripteur de fichier à une fonction personnalisée qui ajoute de la couleur à tout ce qui la traverse. Ajouter à la suite de votre .bashrc

export COLOR_RED="$(tput setaf 1)"
export COLOR_RESET="$(tput sgr0)"

exec 9>&2
exec 8> >(
    Perl -e '$|=1; while(sysread STDIN,$a,9999) {print 
"$ENV{COLOR_RED}$a$ENV{COLOR_RESET}"}'
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; } 
trap "redirect;" DEBUG
Prompt_COMMAND='undirect;'

Alors qu'est-ce qui se passe? L'interruption de débogage est exécutée juste avant et immédiatement après l'exécution d'une commande. stderr est donc redirigé avant l'exécution d'une commande pour activer la sortie rouge. Prompt_COMMAND est évalué avant que l'invite ne s'affiche et, avec cela, je restaure stderr à son état normal. Cela est nécessaire car PS1 et PS2 (votre invite) sont imprimés sur stderr et je ne souhaite pas d'invite rouge. le tour est joué, sortie rouge sur stderr !

12
gospes

Vous devriez vérifier stderred: https://github.com/sickill/stderred

7
sickill

Oui ce n'est pas possible nativement. Vous devrez pirater la gestion de tty (dans le noyau).

J'ai en quelque sorte fini une petite enveloppe C avant d'avoir vu les autres réponses: -) Peut-être un bug, et les valeurs sont codées en dur, ne l'utilisez pas sauf pour les tests.

#include "unistd.h"
#include "stdio.h"
#include <sys/select.h>

int main(int argc, char **argv)
{

char buf[1024];
int pout[2], perr[2];
pipe(pout); pipe(perr);

if (fork()!=0)
{
    close(1); close(2);
    dup2(pout[1],1); dup2(perr[1],2);
    close(pout[1]); close(perr[1]);
    execvp(argv[1], argv+1);
    fprintf(stderr,"exec failed\n");
    return 0;
}

close(pout[1]); close(perr[1]);

while (1)
{
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(pout[0], &fds);
    FD_SET(perr[0], &fds);
    int max = pout[0] > perr[0] ? pout[0] : perr[0];
    int v = select(max+1, &fds, NULL, NULL, NULL);
    if (FD_ISSET(pout[0], &fds))
    {
        int r;
        r = read(pout[0], buf, 1024);
        if (!r) {close(pout[0]); continue;}
        write(1, "\033[33m", 5);
        write(1, buf, r);
        write(1, "\033[0m", 4);
    }
    if (FD_ISSET(perr[0], &fds))
    {
        int r;
        r = read(perr[0], buf, 1024);
        if (!r) {close(perr[0]); continue;}
        write(2, "\033[31m", 5);
        write(2, buf, r);
        write(2, "\033[0m", 4);
    }

    if (v <= 0) break;
}

return 0;
}

Edit: Comparé à la solution Shell, celui-ci conservera plus souvent l’ordre des lignes/caractères. (Il n'est pas possible d'être aussi précis que la lecture directe d'un tty.) Frapper ^ C ne montrera pas un message d'erreur laid, et il se comporte correctement sur cet exemple:

./c_color_script sh -c "while true; do (echo -n a; echo -n b 1>&2) done"
4
Stéphane Gimenez

Je suis surpris que personne n'ait vraiment compris comment colorer les flux stdio. Cela colorera stderr red pour l’ensemble du (sous) Shell:

exec 3>&2
exec 2> >(sed -u 's/^\(.*\)$/'$'\e''[31m\1'$'\e''[m/' >&3)

Dans ce cas, &3 contiendra le flux stderr d'origine.

Vous ne devez transmettre aucune commande à exec, mais uniquement aux redirections. Dans ce cas particulier, exec remplace les flux stdio du (sous) Shell actuel par ceux qu’il reçoit.

Il y a quelques mises en garde:

  • Étant donné que sed fonctionnera de manière persistante dans un sous-shell parallèle, toute sortie directe qui suivra immédiatement une écriture sur le stodo coloré sera probablement supérieure à sed à tty.
  • Cette méthode utilise un descripteur de fichier FIFO; FIFO les nœuds ne traitent que des lignes. Si vous n'écrivez pas de saut de ligne dans le flux, votre sortie sera mise en mémoire tampon jusqu'à ce qu'une nouvelle ligne soit rencontrée. Ce n'est pas la mise en mémoire tampon de la part de sed: c'est comment fonctionnent ces types de fichiers.

Le plus gênant des avertissements est le premier, mais une condition de concurrence critique peut être plus ou moins évitée en appliquant un traitement similaire à toutes les sorties, même si vous utilisez la couleur par défaut.

Vous pouvez effectuer un traitement similaire pour des commandes simples en créant un lien vers la même commande sed avec l'opérateur de canal normal (|). Les chaînes distribuées étant exécutées de manière synchrone, aucune situation de concurrence critique ne se produira, bien que la dernière commande d'une chaîne de distribution reçoive son propre sous-shell par défaut.

4
Zenexer

En développant la réponse donnée par @gospes, j'ai ajouté la fonctionnalité permettant d'imprimer des lignes partielles sans attendre une nouvelle ligne et quelques commentaires. Permet une meilleure sortie de wget ou de la saisie dans un shell interactif.

exec 9>&2
exec 8> >(
    while [ "$r" != "1" ]; do
        # read input, no field separators or backslash escaping, 1/20th second timeout
        IFS='' read -rt 0.05 line
        r=$?
        # if we have input, print the color change control char and what input we have
        if ! [ "${#line}" = "0" ]; then
            echo -ne "\e[1;33m${line}"
        fi
        # end of line detected, print default color control char and newline
        if [ "$r" = "0" ] ; then
            echo -e "\e[0m"
        fi
        # slow infinite loops on unexpected returns - shouldn't happen
        if ! [ "$r" = "0" ] && ! [ "$r" = "142" ]; then
            sleep 0.05
        fi
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
Prompt_COMMAND='undirect;'

J'ai utilisé le jaune vif (1; 33) mais vous pouvez le remplacer par n'importe quoi, rouge par exemple (31) ou rouge vif (1; 33), et j'ai choisi arbitrairement 0,05 seconde pour une nouvelle vérification de fin de ligne et de pause. sur les codes de retour inattendus (jamais trouvé); il pourrait probablement être abaissé ou éventuellement supprimé de la commande de lecture.

0
RyanArr