Come si colorano solo alcune parole chiave per uno script bash?


10

Sto eseguendo un codice di test unitario. Il codice di unit test emette un testo normale. C'è molto del testo, quindi voglio evidenziare per l'utente importanti parole chiave.

In questo caso le parole chiave sono "PASS" e "FAIL".

Come si colora "PASS" per essere verde e "FAIL" per essere rosso?


Hai pensato di sopprimere stdout / stderr dei test che superano e stampare solo stdout / stderr per i test falliti? PHPUnit fa questo, o è configurabile per farlo. Ho scoperto che questo approccio funziona abbastanza bene, YMMV.
Clayton Stanley,

Risposte:


8

supercat sembra fare quello che stai cercando.

Confezione: supercat
Descrizione-it: programma che colora il testo per terminali e HTML
 Supercat è un programma che colora il testo in base alla corrispondenza regolare
 Espressioni / stringhe / caratteri. Supercat supporta anche l'output html
 come testo ASCII standard. A differenza di alcuni programmi di colorazione del testo che
 esiste, Supercat non richiede di essere un programmatore di
 stabilire regole di colorizzazione.
Pagina iniziale: http://supercat.nosredna.net/

Non sembra esserci alcun modo per dirgli cosa colorare sulla riga di comando, devi specificare un file di configurazione.

Mi sembra di ricordare che esisteva un programma chiamato "hilite" o "hl" che evidenziava il testo che corrispondeva a un modello (come grep --colour, ma mostrava anche linee non corrispondenti), ma non riuscivo a trovarlo quando lo cercavo.

Infine, GNU greppuò essere usato per evidenziare i motivi, ma può essere usato solo un colore (cioè non puoi avere PASS in verde e FAIL in rosso, entrambi verrebbero evidenziati con lo stesso colore).

Inoltra i tuoi dati attraverso qualcosa del genere:

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

Questo esempio usa egrep (aka grep -E), ma -Gfunzionano anche regexp di base, -Fstringhe fisse e -PPCRE.

Tutte le partite verranno evidenziate. L'impostazione predefinita è rossa o imposta GREP_COLOR env var.

La chiave di questo lavoro è che il finale |$nel modello corrisponde alla fine della linea (cioè tutte le linee corrispondono) in modo che tutte le linee vengano visualizzate (ma non colorate).

Il \bsono i marcatori di parola-di confine in modo che corrisponda ad esempio, FAIL, ma non il fallimento. non sono necessari, quindi rimuovili se vuoi abbinare le parole parziali.

Ecco lo script wrapper di esempio per supercat che ho scritto ieri. Funziona, ma scrivendolo, ho scoperto che Supercat non ha alcuna opzione per le ricerche senza distinzione tra maiuscole e minuscole. IMO, che rende il programma significativamente meno utile. Tuttavia, ha semplificato notevolmente lo script perché non dovevo scrivere un'opzione '-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"

1
A proposito, se hai usato supercatpotresti modificare lo script che hai pubblicato per generare un file di configurazione temporaneo basato sugli arg della riga di comando e quindi chiamare spc -c /path/to/your/temp/config. Ciò consentirebbe di specificare facilmente colori diversi per motivi diversi sulla riga di comando.
CAS

anche a proposito, ho scritto un semplice script wrapper per farlo. devo andare a lavorare ora ma lo pulirò, aggiungerò alcuni commenti, ecc. e lo posterò più tardi oggi.
CAS

5

Ecco uno script di uso generale per colorare i pattern regex (probabilmente ha bisogno di qualche ritocco):

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

Denominalo hilite.she usalo in questo modo:

$ ./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

L'ho fatto community wikiperché la mia soluzione non funziona.
Trevor Boyd Smith,

Un modo semplice per testare è creare un altro script che riproduca il testo con le parole chiave "PASS" e "FAIL". Questo sarebbe quindi chiamato da questo script.
Trevor Boyd Smith,

Leggermente fuori tema, ma devo sottolineare che l'analisi dell'output di $BINvia evalnella sostituzione del comando è probabilmente fragile, ingombrante e decisamente pericoloso. STDOUT è uno stream ed è generalmente pensato per essere elaborato da strumenti simili a stream. Cercare di catturare un intero flusso in una variabile non è molto efficiente. Inoltre, evalè pericolosamente potente , quindi usalo solo se sai davvero cosa stai facendo.
jw013,

Ho sostituito la sceneggiatura con una funzionante.
angus,

@angus, IMO davvero molto bello e impressionante.
Trevor Boyd Smith,

3

Incorporare stringhe arbitrarie (come l' tputoutput) nelle sedespressioni sostitutive è problematico perché è necessario assicurarsi (mediante l'escape) che la stringa sia sedsintassi valida , che è la maggiore complessità che è meglio evitare. Vorrei usare awkinvece. Proprio come un esempio:

{ 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 
      }}'

La chiave è assegnare le tputsequenze alle awkvariabili, fatte qui usando le -vopzioni.


Ho eseguito il tuo programma e fa ciò che è necessario. Huzzah! Ma sfortunatamente non sono un guru awk, quindi trovo difficile capire cosa sta succedendo. Correggimi dove sbaglio. Per ogni nuova riga viene eseguito awk? Il ciclo for nel codice sorgente di awk sta facendo: "per tutti i token nella linea"? quindi elabora ogni token?
Trevor Boyd Smith,

Awk pensa in termini di campi e record. Per impostazione predefinita, i record sono righe di testo e i campi non sono spazi bianchi. Uno awkscript è costituito da una serie di blocchi (la roba tra il {}livello superiore). Ogni blocco è associato a una condizione - nel mio post sopra non ce n'è nessuno perché voglio agire incondizionatamente su ogni riga di input. Awk opera leggendo i record da file o input standard (in questo caso è STDIN b / c non sono stati forniti nomi di file) e li abbina su ogni blocco in ordine, eseguendo il blocco sul record se la condizione corrisponde.
jw013,

2

usa printf:

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

per esempio.

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

l'ultimo \naggiunge il carattere di nuova riga.

per vedere i possibili valori di provare qualcosa del tipo:

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

oltre al colore puoi aggiungere modificatori come grassetto o sottolineato o testo colorato con sfondo colorato combinando i numeri. Per creare testo blu con sfondo grigio che è sottolineato e barrato attraverso l'uso:

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

Saluti,

/ B2S


2

Se sei felice di installare uno script BASH e ack, il hhlighterpacchetto ha utili colori predefiniti e un'interfaccia semplice https://github.com/paoloantinori/hhighlighter :

highlight esempio

Puoi usarlo in questo modo per evidenziare le righe che iniziano con FAIL:

h -i 'FAIL.*'

o che contengono FAIL:

h -i '.*FAIL.*'

o per varie voci di registro comuni:

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

E, naturalmente, per un singolo colore (rosso) con un semplice grep:

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

Il ^partite ogni linea, ma è un matcher speciale e stampa l'intera linea. L'espressione da evidenziare è definita dopo a |. Ulteriori espressioni possono essere aggiunte purché separate |.

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.