Comment coloriser uniquement certains mots-clés pour un script bash?

10

J'utilise un code de test unitaire. Le code de test unitaire génère du texte normal. Il y a beaucoup de texte, donc je veux mettre en évidence pour l'utilisateur des mots clés importants.

Dans ce cas, les mots clés sont "PASS" et "FAIL".

Comment coloriez-vous "PASS" pour qu'il soit vert et "FAIL" pour qu'il soit rouge?

Trevor Boyd Smith
la source
Avez-vous pensé à supprimer stdout / stderr des tests réussis et à n'imprimer stdout / stderr qu'en cas d'échec des tests? PHPUnit fait cela, ou est configurable pour ce faire. J'ai trouvé que cette approche fonctionne assez bien, YMMV.
Clayton Stanley

Réponses:

8

supercat semble faire ce que vous cherchez.

Paquet: supercat
Description-fr: programme qui colorise le texte pour les terminaux et HTML
 Supercat est un programme qui colorise le texte en fonction de la correspondance régulière
 expressions / chaînes / caractères. Supercat prend également en charge la sortie HTML
 comme texte ASCII standard. Contrairement à certains programmes de colorisation de texte
 existe, Supercat ne vous oblige pas à être un programmeur pour
 faire des règles de colorisation.
Page d'accueil: http://supercat.nosredna.net/

Il ne semble pas y avoir de moyen de lui dire quoi coloriser sur la ligne de commande, vous devez spécifier un fichier de configuration.

Je semble me rappeler qu'il existait auparavant un programme appelé `` hilite '' ou `` hl '' qui mettait en évidence du texte correspondant à un modèle (comme grep --colour, mais affichant également des lignes non correspondantes), mais je ne pouvais pas le trouver lorsque je le cherchais.

Enfin, GNU greppeut être utilisé pour mettre en évidence les motifs - mais une seule couleur peut être utilisée (c'est-à-dire que vous ne pouvez pas avoir PASS en vert et FAIL en rouge, les deux seraient mis en surbrillance avec la même couleur).

Dirigez vos données via quelque chose comme ceci:

egrep --color "\b(PASS|FAIL)\b|$"

Cet exemple utilise egrep (aka grep -E), mais -Gles expressions rationnelles de base, -Fles chaînes fixes et -PPCRE fonctionnent également.

Tous les matchs seront mis en évidence. La valeur par défaut est rouge ou définissez la variable d'environnement GREP_COLOR.

La clé de ce travail est que la finale |$du motif correspond à la fin de la ligne (c'est-à-dire que toutes les lignes correspondent) de sorte que toutes les lignes seront affichées (mais pas colorisées).

Ce \bsont des marqueurs de limite de mot pour qu'il corresponde par exemple à FAIL mais pas à FAILURE. ils ne sont pas nécessaires, supprimez-les donc si vous souhaitez faire correspondre des mots partiels.

Voici l'exemple de script wrapper pour supercat que j'ai écrit hier. Cela fonctionne, mais en l'écrivant, j'ai découvert que supercat n'a pas d'option pour les recherches non sensibles à la casse. OMI, ce qui rend le programme beaucoup moins utile. Cependant, cela a grandement simplifié le script car je n'avais pas à écrire une option '-i' :)

#! /bin/bash 

# Requires: tempfile from debian-utils, getopt from util-linux, and supercat

SCRIPTNAME=$(basename $0)
CFGFILE=$(tempfile -p spc)

usage() {
  cat <<__EOF__
Highlight regexp patterns found on stdin or files specified on command
line with specified colours.

Usage: $SCRIPTNAME [ --colour "pattern" ...] [FILE]

Options:

        -k,--black   regexp
        -r,--red     regexp
        -g,--green   regexp
        -y,--yellow  regexp
        -b,--blue    regexp
        -m,--magenta regexp
        -c,--cyan    regexp
        -w,--white   regexp

Example:

    run-script.sh | $SCRIPTNAME --green PASS --red FAIL

__EOF__
  exit 0
}


# Format definition from the spc man page:
#1234567890123456789012345678901234567890123456789012345
#HTML Color Name      Col A N T RE / String / Characters
FMT="%-20s %3s %1s %1s %1s (%s)\n"

add_color_to_config() {
  COLOR="$1"
  PATTERN="$2"

  printf "$FMT" "$COLOR" "$COLOR" - 0 r "$PATTERN" >> "$CFGFILE"
}


# uses the "getopt" program from util-linux, which supports long
# options. The "getopts" built-in to bash does not.
TEMP=$(getopt \
       -o 'hk:r:g:y:b:m:c:w:' \
       -l 'help,black:,red:,green:,yellow:,blue:,magenta:,cyan:,white:' \
       -n "$0" -- "$@")

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do
    case "$1" in
        -k|--bla*)       add_color_to_config blk "$2" ; shift 2 ;;
        -r|--red)        add_color_to_config red "$2" ; shift 2 ;;
        -g|--gre*)       add_color_to_config grn "$2" ; shift 2 ;;
        -y|--yel*)       add_color_to_config yel "$2" ; shift 2 ;;
        -b|--blu*)       add_color_to_config blu "$2" ; shift 2 ;;
        -m|--mag*)       add_color_to_config mag "$2" ; shift 2 ;;
        -c|--cya*)       add_color_to_config cya "$2" ; shift 2 ;;
        -w|--whi*)       add_color_to_config whi "$2" ; shift 2 ;;

        -h|--hel*)       usage ; exit 0 ;;

        --)         shift ; break ;;

        *)          echo 'Unknown option!' ; exit 1 ;;
    esac
done

spc -R -c "$CFGFILE" "$@"
rm -f "$CFGFILE"
cas
la source
1
BTW, si vous l'avez utilisé, supercatvous pouvez modifier le script que vous avez publié pour générer un fichier de configuration temporaire basé sur les arguments de la ligne de commande, puis appeler spc -c /path/to/your/temp/config. Cela vous permettrait de spécifier facilement différentes couleurs pour différents motifs sur la ligne de commande.
cas
aussi btw, j'ai écrit un script wrapper simple pour le faire. Je dois me rendre au travail maintenant, mais je vais le nettoyer, ajouter des commentaires, etc. et le poster plus tard dans la journée.
cas
5

Voici un script à usage général pour coloriser les motifs regex (a probablement besoin de quelques retouches):

#! /bin/bash

color_to_num () {
  case $1 in
    black)  echo 0;;
    red)    echo 1;;
    green)  echo 2;;
    yellow) echo 3;;
    blue)   echo 4;;
    purple) echo 5;;
    cyan)   echo 6;;
    white)  echo 7;;
    *)      echo 0;;
  esac
}

# default values for foreground and background colors
bg=
fg=
bold="$(tput bold)"
italics=""
boundary=""

while getopts f:b:sli option; do
  case "$option" in
    f) fg="$OPTARG";;
    b) bg="$OPTARG";;
    s) bold="";;
    l) boundary=".*";;
    i) italics="$(tput sitm)";;
  esac
done

shift $(($OPTIND - 1))

pattern="$*"

if [ -n "$fg" ]; then
  fg=$(tput setaf $(color_to_num $fg))
fi
if [ -n "$bg" ]; then
  bg=$(tput setab $(color_to_num $bg))
fi

if [ -z "$fg$bg" ]; then
  fg=$(tput smso)
fi

sed "s/${boundary}${pattern}${boundary}/${bold}${italics}${fg}${bg}&$(tput sgr0)/g"

Nommez-le hilite.shet utilisez-le de cette façon:

$ ./BIN_PROGRAM | hilite.sh -f green PASS | hilite.sh -f red FAIL

$ # Here is an example one liner
$ echo -e "line 1: PASS\nline 2: FAIL" | hilite.sh -f green PASS | hilite.sh -f red FAIL
Trevor Boyd Smith
la source
Je l'ai fait community wikiparce que ma solution ne fonctionne pas.
Trevor Boyd Smith
Un moyen simple de tester consiste à créer un autre script qui fait écho au texte avec les mots-clés "PASS" et "FAIL". Cela serait alors appelé par ce script.
Trevor Boyd Smith
Légèrement hors sujet, mais je dois souligner que l'analyse de la sortie de $BINvia evaldans la substitution de commandes est probablement fragile, encombrante et carrément dangereuse. STDOUT est un flux et est généralement destiné à être traité par des outils de type flux. Essayer de capturer un flux entier dans une variable n'est pas très efficace. En outre, il evalest dangereusement puissant , alors n'utilisez-le que si vous savez vraiment ce que vous faites.
jw013
J'ai remplacé le script par un script fonctionnel.
angus
@angus, script IMO vraiment très bon et impressionnant.
Trevor Boyd Smith
3

L'incorporation de chaînes arbitraires (comme la tputsortie) dans les sedexpressions de remplacement est problématique car vous devez vous assurer (en vous échappant) que la chaîne est une sedsyntaxe valide , ce qui est plus complexe à éviter. J'utiliserais à la awkplace. À titre d'exemple:

{ echo line 1: PASS; echo line 2: FAIL; } | 
    awk -v "red=$(tput setaf 1)" -v "green=$(tput setaf 2)" \
        -v "reset=$(tput sgr0)" '
    { for (i = 1; i <= NF; i++) {
           if ($i == "FAIL") printf "%s", red "FAIL" reset;
           else if ($i == "PASS") printf "%s", green "PASS" reset;
           else printf "%s", $i

           if (i == NF) printf "%s", ORS
           else printf "%s", OFS 
      }}'

L'essentiel est d'assigner les tputséquences aux awkvariables, faites ici en utilisant les -voptions.

jw013
la source
J'ai exécuté votre programme et il fait ce qui est nécessaire. Huzzah! Mais malheureusement, je ne suis pas un gourou awk donc j'ai du mal à comprendre ce qui se passe. Corrigez-moi là où je me trompe. Pour chaque nouvelle ligne awk est exécutée? La boucle for du code source awk fait: "pour tous les jetons de la ligne"? puis il traite chaque jeton?
Trevor Boyd Smith
Awk pense en termes de champs et d'enregistrements. Par défaut, les enregistrements sont des lignes de texte et les champs ne sont pas des espaces. Un awkscript se compose d'un ensemble de blocs (le truc entre le {}niveau supérieur). Chaque bloc est associé à une condition - dans mon article ci-dessus, il n'y en a pas parce que je veux agir inconditionnellement sur chaque ligne d'entrée. Awk fonctionne en lisant des enregistrements à partir de fichiers ou d'une entrée standard (dans ce cas, il s'agit de STDIN b / c, aucun nom de fichier n'a été fourni), et en les comparant à chaque bloc dans l'ordre, en exécutant le bloc sur l'enregistrement si la condition correspond.
jw013
2

utilisez printf:

printf "\e[%sm%s\e[00m\n" <some_number> <text_in_colour>

par exemple.

printf "\e[%sm%s\e[00m\n" 32 yodle

le dernier \najoute le caractère de nouvelle ligne.

pour voir les valeurs possibles d'essayer quelque chose comme:

for i in {0..9} {30..38} {90..98} {100..108};
do
    printf "%d:\e[%sm%s\e[00m\n" $i "$i" yodle;
done

en plus de la couleur, vous pouvez ajouter des modificateurs comme du texte en gras ou souligné ou coloré avec un arrière-plan coloré en combinant les chiffres. Pour créer du texte bleu avec un arrière-plan gris souligné et barré par l'utilisation:

printf "\e[%sm%s\e[00m\n" "4;9;34;107" yodle

À votre santé,

/ B2S

Born2Smile
la source
2

Si vous êtes heureux d'installer un script BASH et ack, le hhlighterpackage a des couleurs par défaut utiles et une interface simple https://github.com/paoloantinori/hhighlighter :

exemple de mise en évidence

Vous pouvez l'utiliser comme tel pour mettre en surbrillance les lignes commençant par FAIL:

h -i 'FAIL.*'

ou qui contiennent FAIL:

h -i '.*FAIL.*'

ou pour diverses entrées de journal courantes:

h -i '.*FAIL.*' '.*PASS.*' '.*WARN.*'

Et bien sûr, pour une seule couleur (rouge) avec grep uni:

$ printf 'Pass: good job\nFail: bad job\n' | grep -Ei --colour=auto '^|fail.*'

Le ^correspond à chaque ligne, mais est un matcher spécial et imprime la ligne entière. L'expression à mettre en évidence est définie après a |. D'autres expressions peuvent être ajoutées tant qu'elles sont séparées par |.

Andy
la source