Wielokolorowy Grep

30

Próbuję uzyskać każde polecenie grep, aby podświetlić jego wyniki w innym kolorze. Mogę to zrobić ręcznie za pomocą takiej linii:

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

Każda cpostać zostanie podświetlona na zielono, a każda opostać zostanie wyróżniona na czerwono itp.

Aby ten przykład zadziałał, musisz upewnić się, że zawsze --color=alwaysużywasz poleceń grep. Ustawiłem to w moim, .bashrc więc grep zawsze będzie miał kolory:

export GREP_OPTIONS='--color=always'


To, co próbuję osiągnąć, to owinięcie tej funkcji aliasem, dzięki czemu mogę po prostu zadzwonić grepi za GREP_COLORSkażdym razem mieć inną wartość. Rozumiem rozważanie wielu powłok dla każdego nowego grep potokowego i staram się to obejść, tworząc niektóre pliki (po jednym dla każdego koloru), aby wskazać, że były już używane.

Podjąłem kilka prób, ale o dziwo, ta wydaje się działać „najlepiej”. Mam to w moim .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 "$@"
}

Używam tego aliasu tak:

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

Wyniki są całkiem fajne. Istnieją jednak pewne błędy, które są nieco inne za każdym razem. Oto kilka zrzutów ekranu:

Wygląda na to, że gdy powłoka przechodzi przez każde polecenie potoku, poprzednia funkcja jeszcze nie zakończyła wykonywania. Próbuje usunąć pliki, które już nie istnieją. Nie jestem zbyt pewien, skąd pochodzą te inne command not foundbłędy.

Jak widać, wprowadziłem kilka waitpoleceń, aby spróbować zakończyć manipulację plikami, ale nie wydaje się to działać zbyt dobrze. Inną rzeczą, którą już wypróbowałem, jest użycie pamięci współdzielonej, /dev/shmale przyniosło to podobne wyniki.

Jak mogę uzyskać pożądane wyniki?

Uwaga:

Szukam odpowiedzi, które po prostu zawiną polecenie grep, ponieważ ma wiele funkcji, których chcę użyć i zamierzam wstawić inną logikę między potokami, więc nie chcę podawać wszystkich wyszukiwanych haseł jednocześnie. Nie szukam też innych narzędzi typu „grep like”. Przepraszam @terdon, który już opublikował niesamowitą propozycję perla.

Lix
źródło
Twój pierwszy przykład nie pasuje do tego, co wyjaśniasz w pozostałej części, mówiąc tylko.
Bernhard,
@bernhard Nie rozumiem, co masz na myśli .. Mam zamiar użyć mojego aliasu jako części większego polecenia za pomocą pipingu ... Daj mi znać, o jakiej sprzeczności mówisz ...
Lix
Myślałem, że też chcesz użyć swojego aliasu poza rurami. W każdym razie twój pierwszy przykład nie działa dla mnie. Próbowałeś alias mg="mygrep; grep"?
Bernhard,
@Bernhard - Pracuję nad urządzeniem Ubuntu 12.04. Nie zdziwiłbym się, gdyby istniały niewielkie różnice ... Wypróbowałem twoją sugestię, problem polega na tym, że mygrep;zmienia się w nowe polecenie samo w sobie i strumień danych ginie. Przychodząca rura z niego lszostanie przekazana mygrep;i nie będzie grep. Przynajmniej tak to rozumiem.
Lix,
@Bernhard - Ach .. Chyba wiem, dlaczego ci to nie zadziałało. Musisz upewnić się, że masz --color=alwayswszystkie polecenia grep. Ustawiłem to globalnie w swoim .bashrc. Zredagowałem to w poście.
Lix,

Odpowiedzi:

6

Oto inne podejście. Mam mały skrypt Perla, który już opublikowałem w innej odpowiedzi, która podświetli wzory dostarczone przez użytkownika w różnych kolorach. Nieznacznie zmodyfikowana wersja skryptu będzie działać następująco 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;
}

Jeśli zapiszesz ten skrypt cgrepgdzieś w swoim PATHpliku i sprawisz, że będzie on wykonywalny, możesz określić do 10 różnych wzorów, z których każdy zostanie wydrukowany w innym kolorze:

wprowadź opis zdjęcia tutaj

$ 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
źródło
5

Każde wywołanie grepw potoku działa w osobnej powłoce, więc musisz przekazać między nimi pewien stan. Poniższe rozwiązanie jest prostym sposobem radzenia sobie z tym za pomocą pliku z indeksem kolorów i pliku blokady, który zapewnia, że ​​jednoczesne połączenia nie będą odczytywać tej samej wartości:

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

Przetestuj, zakładając, że nazwałeś swoją kopię cgrepi umieściłeś ją na PATH:

echo foobarbaz | cgrep foo | cgrep bar | cgrep baz
10b0
źródło
Jedynymi zmiennymi, które naprawdę trzeba zachować, są COLOR_INDEXi GREP_COLORS. Próbowałem wyeksportować je pod koniec funkcji bez powodzenia. Czy o to ci chodziło? Po prostu mieć export VAR, prawda?
Lix,
Och - i tak ... głupia literówka z COLOR_TOGGLE. Dzięki za złapanie :)
Lix
1
@ l0b0 Eksportowanie też dla mnie nie działa, muszę na razie głosować, dopóki nie odpowie na pytanie.
Bernhard
@Bernhard Czy to działa dla Ciebie?
l0b0,
Dzięki za wkład! Uwzględniłem twoją grep "$@"sugestię - uporządkowałem alias, aby uruchomić funkcję, a następnie grep.
Lix
1

Jeśli jesteś dobry w wyrażeniach regularnych, możesz chcieć sprawdzić grc i grcat. grc nazywa grcat.

grcat używa plików konfiguracyjnych, w których można dodawać wyrażenia regularne w celu dopasowania tekstu wyświetlanego w każdym kolorze. Ma również wiele innych opcji. Domyślnie koloruje systemowe pliki dziennika.

W zależności od tego, co masz na myśli w końcowym skrypcie, możesz pokolorować wydruk za pomocą jednego polecenia.

Sztuką jest prawidłowe określenie wyrażeń regularnych dla każdego „pola” w źródle danych. Będzie to o wiele łatwiejsze, jeśli dane będą miały stosunkowo jednolitą strukturę.

Ostatnim razem, gdy spróbowałem, nie zaszedłem za daleko, ale mogę spróbować jeszcze raz, ponieważ jestem trochę lepszy w wyrażeniach regularnych niż wtedy.

Istnieje również polecenie tput, którego można użyć do przesłania informacji (np. Zmiany kolorów) bezpośrednio do urządzenia końcowego.

Oba podejścia są opisane w poniższym poście. Mówi o poleceniu find, ale może być zastosowane do wyniku dowolnego polecenia.

Kolorowe wyjście FIND?

Joe
źródło