2013-02-06 9 views
6

Come faccio a rispettare la grep rispetto ai colori ANSI quando si estrae l'output in pipe? Sono felice di usare qualcos'altro (perl?) Invece di grep.grep attraverso testo colorato, ad es. gcc | colorgcc | grep regexp

mio usercase: Voglio

gcc foobar.c | colorgcc | grep regexp 
ls --color | grep filename 

lavoro bene con i colori (su un terminale UNIX utilizzando ANSI sfugge).

Gli esempi di test di comportamento che voglio:

echo -e "he\e[35mllo\e[00m" world |grep hell ==> he\e[35mllo\e[00m world 
echo -e "\e[35m removed line\nhello\e[00m" world |grep hell ==> \e[35mhello\e[00m world 
echo -e "\e[35m rem\e[1moved line\nhello\e[00m" world | grep hell ==> \e35m\e1mhello\e[00m world 

Attualmente la prima linea dà la stringa vuota, e la seconda dà stringa 'ciao \ e [mondo 00m' uncolorised. Qui \ e [35m e \ e00m sono modificatori di colore (attributo): il colore di una lettera è determinato dalle ultime sequenze di escape di colore (attributo) di forma \ e [P1; P2; .. m dove P1, P2, ecc sono sequenza di cifre; \ e [P1m \ e [P2m è equivalente a \ e [P1; P2m. \ e [0m rende il colore predefinito e dimentica tutte le precedenti sequenze \ e [.. m: \ e [34m \ e [0m equivale a \ e [0m. Ci sono diversi attributi indipendenti (audacia, colore dello sfondo, colore di primo piano/lettera); ogni numero in una sequenza di escape interessa solo uno di essi. Quindi \ e [1m \ e [35m equivale a \ e [1; 35m ma non \ e [35; 1m né \ e [35m; tuttavia, \ e [34m \ e [35m sono equivalenti a \ e [35m perché entrambi influenzano lo stesso effetto (vale a dire, il colore della lettera/foregrnound).

+2

primo tentativo di una soluzione: 'foobar.c gcc | colorgcc | meno -R +/regexp'. 'less -R' capisce i codici colore e cercherà intorno a loro. – nneonneo

risposta

2

Questo è un problema davvero interessante, ecco cosa mi è venuto in mente. E 'abbastanza brutto ma sembra ottenere il lavoro fatto:

sed -n '1s/^/\x1b[0m/;H;x;s/\n//;p;s/.*\(\x1b\[[0-9]*m\(;[0-9]*m\)*\).*/\1/;h' | 
    grep `sed 's/./\0\\\\(\x1b\\\\[[0-9]*m\\\\(;[0-9]*m\\\\)*\\\\)*/g' <<< hell` 

Il termine che si sta cercando potrebbe essere collocato proprio alla fine (al posto di "inferno"), qui ci sono alcuni esempi con il testo hai fornito (utilizzando hexdump per mostrare i colori):

$ echo -e "he\e[35mllo\e[00m" world | 
> sed -n '1s/^/\x1b[0m/;H;x;s/\n//;p;s/.*\(\x1b\[[0-9]*m\(;[0-9]*m\)*\).*/\1/;h' | 
> grep `sed 's/./\0\\\\(\x1b\\\\[[0-9]*m\\\\(;[0-9]*m\\\\)*\\\\)*/g' <<< hell` | 
> hexdump -C 
00000000 1b 5b 30 6d 68 65 1b 5b 33 35 6d 6c 6c 6f 1b 5b |.[0mhe.[35mllo.[| 
00000010 30 30 6d 20 77 6f 72 6c 64 0a     |00m world.| 
0000001a 

$ echo -e "\e[35m removed line\nhello\e[00m" world | 
> sed -n '1s/^/\x1b[0m/;H;x;s/\n//;p;s/.*\(\x1b\[[0-9]*m\(;[0-9]*m\)*\).*/\1/;h' | 
> grep `sed 's/./\0\\\\(\x1b\\\\[[0-9]*m\\\\(;[0-9]*m\\\\)*\\\\)*/g' <<< hell` | 
> hexdump -C 
00000000 1b 5b 33 35 6d 68 65 6c 6c 6f 1b 5b 30 30 6d 20 |.[35mhello.[00m | 
00000010 77 6f 72 6c 64 0a         |world.| 
00000016 

il primo comando sed antepone l'impostazione di colore corrente al all'inizio di ogni riga, che è necessaria per il secondo esempio in cui il colore è impostato su una linea che grep salterà. Il comando sed che è l'argomento di grep inserisce una espressione regolare che corrisponderà a qualsiasi numero di escape di colore tra ogni carattere nel termine di ricerca.

Ecco la versione egrep:

sed -n '1s/^/\x1b[0m/;H;x;s/\n//;p;s/.*\(\x1b\[[0-9]*m\(;[0-9]*m\)*\).*/\1/;h' | 
    egrep `sed 's/./\0(\x1b\\\\[[0-9]*m(;[0-9]*m)*)*/g' <<< hell` 
+0

Grazie! Questo non funziona correttamente: echo -e "\ e [35m rimosso \ e [0m line \ nh \ e [34mel \ e [32mlo \ e [00m" –

+0

oh scusa! no, va bene. ma funziona con espressioni regolari arbitrarie? Non sembra funzionare se sostituisco l'ultimo 'grep' con 'egrep' –

+0

Se si sostituisce grep con egrep, allora è possibile rimuovere tutti i backslash nel secondo comando sed che si trovano immediatamente prima di '(' o ')' . Aggiunto questo in una modifica. –