Como você colorir apenas algumas palavras-chave para um script bash?

10

Estou executando algum código de teste de unidade. O código do teste de unidade gera texto normal. Há muito texto, por isso quero destacar para as palavras-chave importantes do usuário.

Nesse caso, as palavras-chave são "PASS" e "FAIL".

Como você define "PASS" como verde e "FAIL" como vermelho?

Trevor Boyd Smith
fonte
Você já pensou em suprimir stdout / stderr de testes aprovados e imprimir apenas stdout / stderr para testes com falha? O PHPUnit faz isso ou é configurável para fazer isso. Eu descobri que essa abordagem funciona muito bem, YMMV.
Clayton Stanley

Respostas:

8

supercat parece fazer o que você está procurando.

Pacote: supercat
Description-pt: programa que coloriza o texto para terminais e HTML
 O Supercat é um programa que coloriza o texto com base na correspondência regular
 expressões / strings / caracteres. Supercat também suporta saída html
 como texto ASCII padrão. Ao contrário de alguns programas de coloração de texto que
 existir, o Supercat não exige que você seja um programador para
 faça regras de coloração.
Página inicial: http://supercat.nosredna.net/

Não parece haver nenhuma maneira de dizer o que colorir na linha de comando, você precisa especificar um arquivo de configuração.

Lembro-me de que costumava haver um programa chamado 'hilite' ou 'hl' que realçava o texto que correspondia a um padrão (como grep --colour, mas também exibe linhas não correspondentes), mas não o encontrei quando o procurei.

Finalmente, o GNU greppode ser usado para destacar padrões - mas apenas uma cor pode ser usada (ou seja, você não pode ter PASS em verde e FAIL em vermelho, ambos seriam destacados com a mesma cor).

Canalize seus dados através de algo como isto:

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

Este exemplo usa egrep (aka grep -E), mas -Gregexp básico, -Fstring fixa e -PPCRE também funcionam.

Todas as partidas serão destacadas. O padrão é vermelho ou configure a var. Env GREP_COLOR.

A chave para este trabalho é que a final |$no padrão corresponde ao final da linha (ou seja, todas as linhas correspondem), para que todas as linhas sejam exibidas (mas não coloridas).

O \bsão marcadores de limite de palavras para que ele corresponda por exemplo falhar, mas não FALHA. eles não são necessários; portanto, remova-os se desejar corresponder a palavras parciais.

Aqui está o exemplo de script wrapper para supercat que escrevi ontem. Funciona, mas, ao escrevê-lo, descobri que o supercat não tem nenhuma opção para pesquisas que não diferenciam maiúsculas de minúsculas. IMO, isso torna o programa significativamente menos útil. No entanto, simplificou bastante o script, porque não precisei escrever uma opção '-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
fonte
1
BTW, se você usou, supercatpode modificar o script publicado para gerar um arquivo de configuração temporário com base nos argumentos da linha de comando e, em seguida, chamar spc -c /path/to/your/temp/config. Isso permitiria especificar facilmente cores diferentes para diferentes padrões na linha de comando.
cas
btw também, eu escrevi um script wrapper simples para fazer isso. Eu tenho que ir trabalhar agora, mas vou limpá-lo, adicionar alguns comentários, etc e publicá-lo hoje mais tarde.
cas
5

Aqui está um script de uso geral para colorir padrões de regex (provavelmente precisa de 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"

Nomeie-o hilite.she use-o desta maneira:

$ ./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
fonte
Fiz isso community wikiporque minha solução não funciona.
Trevor Boyd Smith
Uma maneira fácil de testar é criar outro script que reproduza o texto com as palavras-chave "PASS" e "FAIL". Isso seria chamado por esse script.
Trevor Boyd Smith
Um pouco fora do tópico, mas devo salientar que analisar a saída da $BINvia evalna substituição de comandos é provavelmente frágil, complicado e absolutamente perigoso. STDOUT é um fluxo e geralmente deve ser processado por ferramentas como fluxo. Tentar capturar um fluxo inteiro em uma variável não é muito eficiente. Além disso, evalé perigosamente poderoso , portanto, use-o apenas se você realmente souber o que está fazendo.
Jw013
Substituí o script por um que funcione.
Angus
@angus, script realmente muito bom e impressionante IMO.
Trevor Boyd Smith
3

A incorporação de cadeias arbitrárias (como tputsaída) em sedexpressões de substituição é problemática, pois é necessário garantir (escapando) que a cadeia seja de sedsintaxe válida , o que evita mais complexidade. Eu usaria em seu awklugar. Apenas como exemplo:

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

A chave é atribuir as tputseqüências às awkvariáveis, feitas aqui usando as -vopções.

jw013
fonte
Eu executei seu programa e ele faz o que é necessário. Huzzah! Mas, infelizmente, não sou um guru inábil, por isso acho difícil entender o que está acontecendo. Corrija-me onde estou errado. Para cada nova linha, o awk é executado? O loop for no código-fonte do awk está executando: "para todos os tokens da linha"? então processa cada token?
Trevor Boyd Smith
Awk pensa em termos de campos e registros. Por padrão, os registros são linhas de texto e os campos não são espaços em branco. Um awkscript consiste em um conjunto de blocos (as coisas entre o {}nível superior). Cada bloco está associado a uma condição - no meu post acima, não há nenhum porque quero agir incondicionalmente em todas as linhas de entrada. O Awk opera lendo registros de arquivos ou de entrada padrão (neste caso, é STDIN b / c, nenhum nome de arquivo foi fornecido) e os associa a cada bloco na ordem, executando o bloco no registro, se a condição corresponder.
jw013
2

use printf:

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

por exemplo.

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

o último \nadiciona o caractere de nova linha.

para ver os possíveis valores de tentar 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

além da cor, você pode adicionar modificadores como negrito, sublinhado ou texto colorido com fundo colorido combinando os números. Para criar texto azul com fundo cinza sublinhado e marcado através do uso:

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

Felicidades,

/ B2S

Born2Smile
fonte
2

Se você estiver feliz em instalar um script BASH e ack, o hhlighterpacote tiver cores padrão úteis e uma interface fácil https://github.com/paoloantinori/hhighlighter :

exemplo de destaque

Você pode usá-lo assim para destacar as linhas que começam com FAIL:

h -i 'FAIL.*'

ou que contenham FAIL:

h -i '.*FAIL.*'

ou para várias entradas de log comuns:

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

E, claro, para uma única cor (vermelho) com grep simples:

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

A ^corresponde a cada linha, mas é uma correspondência especial e imprime a linha inteira. A expressão a ser destacada é definida após a |. Expressões adicionais podem ser adicionadas desde que separadas |.

Andy
fonte