Este recurso de destaque pode ser feito facilmente em grep
com --color
. Mas grep
não é possível fazer correspondência regular e invertida ao mesmo tempo , por exemplo grep foo -v bar ...
(preciso deste recurso, pois ele --color
só pode ser usado na entrada inicial do pipe |
, então o último grep usará os resultados coloridos que grep foo --color=always ... | grep -v "foo bar"
não removerão essas linhas com "foo bar").
Então eu usei awk
como as dicas do link acima, mas falta o recurso de destaque. Podemos extrair os resultados do regex com match
esubstr
onde envolvemos o padrão regex em match
como /\<no issue\>/
. Podemos então destacar apenas o resultado do regex com códigos de cores ANSI por printf substr($0,1,RSTART-1) "\033[1;31m" substr($0,RSTART,RLENGTH) "\033[0m: " substr($0,RSTART+RLENGTH) "\n"
.
P:
Mas match
não funciona para o boolean expr /foo/ && !/bar/
construído por regex exp's no link de referência. Existe uma solução alternativa elegante (talvez com outras ferramentas)?
Pensei em uma solução temporária /foo/ && !/bar/{match($0,/foo/); printf ...}
, mas isso fará com que o regex funcione duas vezes.
Meu comportamento esperado (Aqui 0. "TODO" pode existir no máximo uma vez em uma linha 1. Eu uso *...*
para mostrar o que deve ser destacado 2. Eu considero apenas /pattern1/ && !/pattern_included_totally_by_pattern1/
, então /foo/ && !/o/
oferecido por Ed Morton não é considerado. Isso é como a lógica grep, que eu primeiro grep some e então grep in aqueles resultados obtidos da execução grep anterior):
# fail
$ echo "TODO foo\nTODO" | grep TODO --color=always | grep -v "TODO foo"
*TODO* foo
*TODO*
# expected
$ echo "TODO foo\nTODO" | awk -v RED="\033[1;31m" -v RESET="\033[0m" '/TODO/ && !/TODO foo/ {match($0,/TODO/); printf substr($0,1,RSTART-1) RED substr($0,RSTART,RLENGTH) RESET substr($0,RSTART+RLENGTH) "\n"}'
*TODO*
Eu usaria
perl
aqui. Para destacarfoo
linhas que não contêmfoo bar
:O mesmo que:
$'...'
Ou (assumindo um shell com suporte para o formato de aspas no estilo ksh93 ):Ou para destacar os
foo
s que não são seguidos porbar
.Não
awk
,sed
equivalente, exceto se o seused
for um dos poucos que suportam expressões regulares do tipo Perl, como alguns fazem com-P
(como o ast-open) ou-R
(comossed
o )), no entanto, é mais comum quegrep
implementações suportemperl
expressões regulares do tipo - geralmente com uma-P
opção, como o GNUgrep
faz quando construído com suporte a expressões regulares do tipo Perl por meio da biblioteca PCRE2 (antigamente PCRE), nesse caso você pode fazer:Mas isso também descarta linhas sem correspondência.
Para descartar as linhas que contêm
foo bar
e destacar ocorrências defoo
nas linhas restantes, você pode fazer:Ou:
$'...'
Ou (assumindo um shell com suporte para o formato de aspas no estilo ksh93 ):Mas você também pode fazer:
(
-e '^'
para corresponder a todas as linhas, para que não sejam descartadas,-e foo
para destacarfoo
ocorrências nessas linhas;< file.in A | B > file.out
é comoA < file.in | B > file.out
ouA < file.in | > file.out B
como redirecionamentos podem aparecer em qualquer lugar nas linhas de comando, mas usar o primeiro em pipelines é comum, pois torna o fluxo mais evidente).Para descartar todas as linhas que contêm
foo bar
e todas as que não contêmfoo
e destacarfoo
as restantes:Detalhes sobre a
perl
sintaxe:perl -p
é osed
modo (eperl -n
osed -n
modo) onde o código (aqui fornecido como uma-e
expressão inline como emsed
) é executado para cada linha da entrada com a linha atual em$_
(equivalente aosed
espaço de padrões de com a diferença de que emperl
,$_
contém o delimitador de linha). Vejaperldoc perlrun
para detalhes.s/replacement/replacement/
é semelhante aosed
's, mas com muitas melhorias, em particular expressões regulares com muito mais recursos, que muitas outras linguagens copiaram desde então.(?! foo)
é um desses operadores regexp extras introduzidos pelo perl, que é um operador negativo de look ahead. Vejaperldoc perlre
para detalhes.code unless some-condition
é uma forma curta deunless (some-condition) {code}
que se lê mais como inglês, mas é equivalente de outra forma.unless
sendo comoif !
. Vejaperldoc perlsyn
para detalhes.Sua solução alternativa é perfeitamente aceitável. Note que, como você parece estar usando o gnu awk, você também pode usar
sub()
ougsub()
para substituir uma regexp por uma string, com&
a substituição sendo feita pela parte correspondente:Se você não quiser repetir a expressão regular, você pode dividir os testes
ou desde
sub()
retorna 1 em caso de sucessoou
Às vezes, em sua pergunta, parece que você quer destacar uma linha inteira que satisfaça a condição
/foo/ && !/bar/
que é apenas (substituaRED
eRESET
pelos códigos de cores que desejar):enquanto outras vezes parece que você quer destacar linhas que correspondem
foo
, mas nãofoo bar
quais seriam:ou para evitar escrever
foo
duas vezes:enquanto outras vezes ainda parece que você só quer destacar a parte que corresponde
foo
em uma linha que não contém,bar
que é apenas:Se você quiser algo diferente de qualquer um desses, mostre um exemplo em sua pergunta. Em particular, se você estiver procurando por uma solução geral que possa destacar partes de linhas dadas múltiplas expressões regulares em uma condição composta, explique em sua pergunta a lógica por trás do que destacar e do que não destacar, incluindo para o caso
/foo/ && !/o/
.