Grep multicolorido

30

Estou tentando obter cada comando grep para destacar seus resultados em uma cor diferente. Eu posso fazer isso manualmente com uma linha como esta:

ls -l GREP_COLORS='mt=01;32' grep c | GREP_COLORS='mt=01;31' grep o | GREP_COLORS='mt=01;34' grep n | GREP_COLORS='mt=01;36' grep f

Cada caractere c será destacado em verde e todos os caracteres o serão destacados em vermelho, etc ...

For this example to work you'll need to ensure that you always have --color=always on your grep commands. I've set this in my .bashrc so grep will always have colors:

export GREP_OPTIONS='--color=always'

O que estou tentando realizar é envolver essa funcionalidade com um alias para que eu possa chamar grep e ter um valor GREP_COLORS diferente a cada vez. Eu entendo a consideração de múltiplos shells para cada novo pip grep e estou tentando superar isso criando alguns arquivos (um para cada cor), para indicar que eles já foram usados.

Eu fiz algumas tentativas, mas estranhamente, esta parece funcionar com o "melhor". Eu tenho isso no meu .bashrc :

alias mg="mygrep"
mygrep(){
    # define possible colors
    COLORS=("01;32" "01;31" "01;34" "01;36")
    COUNTER=0
    NUM=0
    # as long as the color has already been used, keep searching
    while [ -f /home/lior/Desktop/mygrep_$NUM ]; do
        # get a random index
        let NUM='shuf --input-range=0-$(( ${#COLORS[*]} - 1 )) | head -1'
        wait ${!}
        $(( COUNTER+=1 ))
        if [ "$COUNTER" -ge ${#COLORS[@]} ]; then
            # remove all color locks
            rm /home/lior/Desktop/mygrep_*
            wait ${!}
        fi
    done
    # mark this color as used
    touch /home/lior/Desktop/mygrep_$NUM
    wait ${!}

    # lets go!
    GREP_COLORS="mt=${COLORS[$NUM]}" grep "$@"
}

Estou usando esse alias assim:

ll | mg c | mg o | mg n | mg f

Os resultados são bem legais. No entanto, existem alguns erros que são ligeiramente diferentes de cada vez. Aqui estão algumas capturas de tela:

Parece que quando o shell passa por cada comando de pipe, a função anterior ainda não terminou sua execução. Ele tenta remover arquivos que não existem mais. Eu não tenho muita certeza de onde esses outros erros command not found estão vindo.

Como você pode ver, eu coloquei alguns comandos wait para tentar deixar a manipulação do arquivo completa, mas isso não parece estar funcionando muito bem. Outra coisa que já tentei é usar a memória compartilhada /dev/shm , mas ela produziu resultados semelhantes.

Como eu poderia conseguir os resultados que quero?

Note:

I am looking for answers that simply wrap the grep command as it has lots of functionality that I'm wanting to use and intend to insert other logic between the pipes, so I don't want to provide all of the search terms at once. I'm also not looking for other "grep like" tools. Sorry to @terdon who has already posted an awesome perl suggestion.

    
por Lix 09.12.2013 / 11:16

3 respostas

5

Cada invocação de grep em um pipe é executada em um shell separado, então você precisará passar algum estado entre eles. A solução a seguir é uma maneira bruta de lidar com isso com um arquivo que mantém o índice de cores e um arquivo de bloqueio que garante que as chamadas simultâneas não leiam o mesmo valor:

#!/usr/bin/env bash
color_index_file=~/.gitcolor
color_index_lock_file=/tmp/$(basename $0)

colors=()
for index in {31..34}
do
    colors+=("01;$index")
done

until mkdir "$color_index_lock_file" 2>/dev/null
do
    :
done

color_index=$(($(cat "$color_index_file" || echo 0) + 1))

if [[ $color_index -ge ${#colors[@]} ]]
then
    color_index=0
fi

printf "$color_index" > "$color_index_file"
rmdir "$color_index_lock_file"

GREP_COLORS="mt=01;${colors[$color_index]}" grep --color=always "$@"

Teste assumindo que você nomeou sua cópia cgrep e a colocou no seu PATH :

echo foobarbaz | cgrep foo | cgrep bar | cgrep baz
    
por 09.12.2013 / 13:39
5

Aqui está uma abordagem diferente. Eu tenho um pequeno script Perl que eu tenho já postado em outra resposta que irá destacar os padrões fornecidos pelo usuário em cores diferentes . Uma versão ligeiramente modificada do script funcionará como grep :

#!/usr/bin/env perl
use Getopt::Std;
use strict;
use Term::ANSIColor; 

my %opts;
getopts('hic:l:',\%opts);
    if ($opts{h}){
      print<<EoF; 
Use -l to specify the pattern(s) to highlight. To specify more than one 
pattern use commas. 

-l : A Perl regular expression to be colored. Multiple expressions can be
     passed as comma separated values: -l foo,bar,baz
-i : makes the search case sensitive
-c : comma separated list of colors;

EoF
      exit(0);
    }

my $case_sensitive=$opts{i}||undef;
my @color=('bold red','bold blue', 'bold yellow', 'bold green', 
       'bold magenta', 'bold cyan', 'yellow on_blue', 
       'bright_white on_yellow', 'bright_yellow on_red', 'white on_black');
if ($opts{c}) {
   @color=split(/,/,$opts{c});
}
my @patterns;
if($opts{l}){
     @patterns=split(/,/,$opts{l});
}
else{
    $patterns[0]='\*';
}

# Setting $| to non-zero forces a flush right away and after 
# every write or print on the currently selected output channel. 
$|=1;

while (my $line=<>) 
{ 
    my $want=0;
    for (my $c=0; $c<=$#patterns; $c++){
    if($case_sensitive){
        if($line=~/$patterns[$c]/){
           $line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ge;
           $want++;
        }
    }
    else{
        if($line=~/$patterns[$c]/i){
          $line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ige;
          $want++;
        }
      }
    }
print STDOUT $line if $want>0;
}

Se você salvar esse script como cgrep em algum lugar no PATH e torná-lo executável, poderá especificar até 10 padrões diferentes, cada um dos quais será impresso em uma cor diferente:

$ cgrep -h
Use -l to specify the pattern(s) to highlight. To specify more than one 
pattern use commas. 

-l : A Perl regular expression to be colored. Multiple expressions can be
     passed as comma separated values: -l foo,bar,baz
-i : makes the search case sensitive
-c : comma separated list of colors;
    
por 09.12.2013 / 16:45
1

Se você é bom com expressões regulares, você pode querer verificar grc e grcat. grc chama grcat.

O grcat usa arquivos de configuração nos quais você pode adicionar expressões regulares para corresponder ao texto a ser exibido em cada cor. Também tem várias outras opções. O padrão é colorir os arquivos de log do sistema.

Dependendo do que você tem em mente para o seu script final, você poderá colorir sua saída com apenas um comando.

O truque é especificar os regexes corretamente para cada "campo" na sua fonte de dados. Isso será muito mais fácil se seus dados forem relativamente uniformes na estrutura.

Da última vez que tentei, não cheguei muito longe, mas talvez tenha outra chance porque sou um pouco melhor em regexes do que naquela época.

Existe também o comando tput que pode ser usado para enviar informações (como alterações de cor) diretamente para o seu dispositivo terminal.

Ambas as abordagens são cobertas pelo post abaixo. Ele fala sobre o comando find, mas pode ser aplicado na saída de qualquer comando.

Saída FIND colorida?

    
por 15.12.2013 / 15:16