Grep multicolorido

30

Estou tentando fazer com que cada comando grep destaque 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 cpersonagem será destacado em verde e todo opersonagem será destacado em vermelho, etc ...

Para que este exemplo funcione, você precisará garantir que sempre tenha --color=alwaysseus comandos grep. Eu configurei isso no meu .bashrc modo grep sempre terá cores:

export GREP_OPTIONS='--color=always'


O que estou tentando realizar é agrupar essa funcionalidade com um alias para que eu possa chamar grepe ter um GREP_COLORSvalor diferente a cada vez. Entendo a consideração de vários shells para cada novo grep canalizado e estou tentando resolver isso criando alguns arquivos (um para cada cor), para indicar que eles já foram usados.

Fiz algumas tentativas, mas estranhamente, essa parece funcionar da melhor maneira. 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 este 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 a cada vez. Aqui estão algumas capturas de tela:

Parece que conforme o shell passa por cada comando de pipe, a função anterior ainda não concluiu sua execução. Ele tenta remover arquivos que não existem mais. Não tenho muita certeza de onde command not foundvêm esses outros erros.

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

Como eu obteria os resultados desejados?

Nota:

Estou procurando respostas que simplesmente envolvam o comando grep, pois ele tem muitas funcionalidades que estou querendo usar e pretendo inserir outra lógica entre os pipes, portanto, não quero fornecer todos os termos de pesquisa de uma só vez. Também não estou procurando outras ferramentas "grep like". Desculpe @terdon, que já postou uma sugestão perl impressionante.

Lix
fonte
Seu primeiro exemplo não corresponde ao que você está explicando na parte restante, apenas dizendo.
Bernhard
@bernhard Eu não entendo o que quer dizer .. Minha intenção é usar o meu alias como parte de um comando maior usando tubulação ... Por favor, deixe-me saber que a contradição que você está falando de ...
Lix
Eu pensei que você queria usar seu apelido fora dos canos também. De qualquer forma, seu primeiro exemplo não funciona para mim. Você tentou alias mg="mygrep; grep"?
Bernhard
@ Bernhard - Estou trabalhando em uma caixa do ubuntu 12.04. Eu não ficaria surpreso se houvesse pequenas diferenças ... Tentei sua sugestão, o problema é que isso se mygrep;transforma em um novo comando e o fluxo de dados se perde. O tubo de entrada do lsseria passado para mygrep;e não para grep. Pelo menos é assim que eu entendo.
Lix
@ Bernhard - Ah .. acho que sei por que não funcionou para você. Você precisa ter certeza de ter --color=alwaystodos os seus comandos grep. Eu coloquei isso globalmente no meu .bashrc. Eu editei isso no post.
Lix

Respostas:

6

Aqui está uma abordagem diferente. Eu tenho um pequeno script Perl que já publiquei em outra resposta que 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 em cgrepalgum lugar do seu PATHarquivo e torná-lo executável, poderá especificar até 10 padrões diferentes, cada um dos quais será impresso em uma cor diferente:

insira a descrição da imagem aqui

$ 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;
terdon
fonte
5

Cada chamada de grepem um pipe é executada em um shell separado, portanto, você precisará passar algum estado entre eles. A solução a seguir é uma maneira simples 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 supondo que você nomeou sua cópia cgrepe coloque-a em PATH:

echo foobarbaz | cgrep foo | cgrep bar | cgrep baz
l0b0
fonte
As únicas variáveis ​​que realmente precisam ser mantidas são COLOR_INDEXe GREP_COLORS. Tentei exportá-los no final da função sem êxito. É isso que você quis dizer? Simplesmente ter export VAR, certo?
Lix
Oh - e sim .. erro de digitação bobo lá com COLOR_TOGGLE. Obrigado por captura-lo :)
Lix
11
@ l0b0 A exportação também não funciona para mim, tenho que reduzir o voto por enquanto até que realmente responda à pergunta.
Bernhard
@ Bernhard Isso funciona para você?
L0b0
Obrigado pela sua contribuição! Eu incorporei sua grep "$@"sugestão - que resolveu o alias para executar a função e depois grep.
Lix
1

Se você é bom com expressões regulares, consulte grc e grcat. O grc chama grcat.

O grcat usa arquivos de configuração onde você pode adicionar expressões regulares para corresponder ao texto a ser exibido em cada cor. Ele 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 script final, você poderá colorir sua saída com apenas um comando.

O truque é especificar corretamente as expressões regulares para cada "campo" na sua fonte de dados. Isso será muito mais fácil se seus dados tiverem uma estrutura relativamente uniforme.

Na última vez que tentei, não cheguei muito longe, mas posso tentar outra vez, porque sou um pouco melhor em regexes do que era na época.

Há 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 abordadas no post abaixo. Ele fala sobre o comando find, mas pode ser aplicado à saída de qualquer comando.

Saída FIND colorida?

Joe
fonte