¿Cómo colorea solo algunas palabras clave para un script bash?

10

Estoy ejecutando un código de prueba de unidad. El código de prueba de la unidad genera texto normal. Hay mucho texto, así que quiero resaltar para el usuario palabras clave importantes.

En este caso, las palabras clave son "PASS" y "FAIL".

¿Cómo coloreas "PASS" para que sea verde y "FAIL" para que sea rojo?

Trevor Boyd Smith
fuente
¿Has pensado en suprimir stdout / stderr de las pruebas que pasan y solo imprimir stdout / stderr para pruebas fallidas? PHPUnit hace esto, o es configurable para hacer esto. He descubierto que este enfoque funciona bastante bien, YMMV.
Clayton Stanley

Respuestas:

8

supercat parece hacer lo que estás buscando.

Paquete: supercat
Description-en: programa que colorea el texto para terminales y HTML
 Supercat es un programa que colorea el texto en función de la coincidencia regular
 expresiones / cadenas / caracteres. Supercat también admite la salida html
 como texto ASCII estándar. A diferencia de algunos programas para colorear texto que
 existe, Supercat no requiere que tengas que ser un programador para
 Hacer reglas de coloración.
Página de inicio: http://supercat.nosredna.net/

No parece haber ninguna forma de decirle qué colorear en la línea de comando, debe especificar un archivo de configuración.

Creo recordar que solía haber un programa llamado 'hilite' o 'hl' que resaltaba el texto que coincidía con un patrón (como grep --colour, pero que también mostraba líneas que no coincidían), pero no pude encontrarlo cuando lo busqué.

Finalmente, GNU grepse puede usar para resaltar patrones, pero solo se puede usar un color (es decir, no se puede PASAR en verde y FALLO en rojo, ambos se resaltarán con el mismo color).

Canalice sus datos a través de algo como esto:

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

Este ejemplo usa egrep (aka grep -E), pero también funciona la expresión regular -Gbásica, -Fla cadena fija y -PPCRE.

Todos los partidos serán resaltados. El valor predeterminado es rojo, o establece el GREP_COLOR env var.

La clave de este trabajo es que la final |$en el patrón coincide con el final de la línea (es decir, todas las líneas coinciden), por lo que todas las líneas se mostrarán (pero no se colorearán).

El \bson marcadores de límite de palabra para que coincida con por ejemplo FALLO FALLO pero no. no son necesarios, así que elimínelos si desea unir palabras parciales.

Aquí está el script de ejemplo de envoltura para supercat que escribí ayer. Funciona, pero al escribirlo, descubrí que Supercat no tiene ninguna opción para las búsquedas que no distinguen entre mayúsculas y minúsculas. OMI, eso hace que el programa sea significativamente menos útil. Sin embargo, simplificó enormemente el script porque no tuve que escribir una opción '-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
fuente
1
Por cierto, si lo usó supercat, podría modificar el script que publicó para generar un archivo de configuración temporal basado en los argumentos de la línea de comandos y luego llamar spc -c /path/to/your/temp/config. Esto le permitiría especificar fácilmente diferentes colores para diferentes patrones en la línea de comando.
cas
También, por cierto, escribí un script de contenedor simple para hacer eso. Tengo que ir a trabajar ahora, pero lo limpiaré, agregaré algunos comentarios, etc. y lo publicaré más tarde hoy.
cas
5

Aquí hay un script de propósito general para colorear los patrones de expresiones regulares (probablemente necesite algunos retoques):

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

Nómbralo hilite.shy úsalo de esta manera:

$ ./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
fuente
Hice esto community wikiporque mi solución no funciona.
Trevor Boyd Smith
Una manera fácil de probar es crear otro script que haga eco del texto con las palabras clave "PASS" y "FAIL". Eso sería llamado por este script.
Trevor Boyd Smith
Ligeramente fuera de tema, pero debo señalar que analizar la salida de $BINvia evalen sustitución de comandos es probablemente frágil, engorroso y francamente peligroso. STDOUT es una secuencia y generalmente está destinado a ser procesado por herramientas similares a la secuencia. Intentar capturar una secuencia completa en una variable no es muy eficiente. Además, evales peligrosamente poderoso , así que solo úsalo si realmente sabes lo que estás haciendo.
jw013
Reemplacé el guión por uno funcional.
angus
@angus, realmente muy bueno e impresionante script IMO.
Trevor Boyd Smith
3

Incrustar cadenas arbitrarias (como tputsalida) en sedexpresiones de reemplazo es problemático porque debe asegurarse (escapando) de que la cadena sea una sedsintaxis válida , lo que es más complejo y es mejor evitarlo. Yo usaría en su awklugar. Solo como un ejemplo:

{ 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 clave es asignar las tputsecuencias a las awkvariables, hecho aquí usando las -vopciones.

jw013
fuente
Ejecuté su programa y hace lo que se necesita. Huzzah! Pero desafortunadamente no soy un gurú awk, así que me resulta difícil entender lo que está sucediendo. Corrígeme donde estoy equivocado. ¿Para cada nueva línea se ejecuta awk? El bucle for en el código fuente awk está haciendo: "para todos los tokens en la línea"? entonces procesa cada token?
Trevor Boyd Smith
Awk piensa en términos de campos y registros. Por defecto, los registros son líneas de texto y los campos no son espacios en blanco. Una awksecuencia de comandos consta de un conjunto de bloques (las cosas entre el {}en el nivel superior). Cada bloque está asociado con una condición: en mi publicación anterior no hay ninguno porque quiero actuar incondicionalmente en cada línea de entrada. Awk funciona leyendo registros de archivos o entradas estándar (en este caso es STDIN b / c no se proporcionaron nombres de archivos), y los compara con cada bloque en orden, ejecutando el bloque en el registro si la condición coincide.
jw013
2

use printf:

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

p.ej.

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

el último \nagrega el carácter de nueva línea.

para ver los posibles valores de probar algo como:

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

Además del color, puede agregar modificadores como negrita o subrayado o texto de color con fondo de color combinando los números. Para crear texto azul con fondo gris subrayado y marcado mediante el uso:

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

Salud,

/ B2S

Born2Smile
fuente
2

Si está contento de instalar un script BASH y ack, el hhlighterpaquete tiene colores predeterminados útiles y una interfaz fácil https://github.com/paoloantinori/hhighlighter :

Ejemplo de hhighlight

Puede usarlo así para resaltar las filas que comienzan con FAIL:

h -i 'FAIL.*'

o que contienen FAIL:

h -i '.*FAIL.*'

o para varias entradas de registro comunes:

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

Y, por supuesto, para un solo color (rojo) con grep simple:

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

La ^partidos cada línea, pero es una matcher especial e imprime toda la línea. La expresión a resaltar se define después de a |. Se pueden agregar más expresiones siempre que estén separadas por |.

Andy
fuente