Interpretar uma variável interpolada como uma string literal em Perl regex

2

Estou usando perl para colorir uma string correspondente

MATCH=aaa
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/($MATCH)/\e[1;31m\e[0m/g"

Isso funciona bem até que eu tente corresponder uma string contendo caracteres especiais, por exemplo

MATCH=/dev/
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/($MATCH)/\e[1;31m\e[0m/g"

A razão é, quando bash substitui $MATCH , perl obtém a seguinte expressão:

perl -pe "s/(/dev/)/\e[1;31m\e[0m/g"

Eu precisaria retroceder os / para fazê-lo funcionar, por exemplo

MATCH=\/dev\/
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/($MATCH)/\e[1;31m\e[0m/g"    

Mas não sei o que MATCH conterá. Pode ser qualquer coisa, + ou ( . Existe uma maneira de dizer perl para tratar os caracteres literalmente, não como uma expressão?

UPDATE:

A utilização da solução sugerida por Joseph R. fornece o seguinte erro:

MATCH=/dev/
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/\Q($MATCH)\E/\e[1;31m\e[0m/g"
Backslash found where operator expected at -e line 1, near ")\"
(Missing operator before \?)
Having no space between pattern and following word is deprecated at -e line 1.
syntax error at -e line 1, near "s/\Q(/dev/)"
Search pattern not terminated at -e line 1.

UPDATE2:

agora não recebo nenhum erro, mas parece não haver correspondência (não há coloração):

# MATCH=/dev/ ; printf "aaa\n/dev/aaa\nccc\n" | perl -pe 's/(\Q$ENV{MATCH}\E)/\e[1;31m\e[0m/g'
aaa
/dev/aaa
ccc
    
por Martin Vegter 25.05.2014 / 23:51

1 resposta

3

Sim, você pode usar o operador quotemeta do Perl com aspas simples:

export MATCH=...
... | perl -pe 's/(\Q$ENV{MATCH}\E)/...

Qualquer coisa entre \Q e \E (ou até o final da regex, se nenhum \E for fornecido) é considerado como está, sem nenhum significado especial anexado aos metacaracteres.

Equivalente:

export MATCH=...
... | perl -pe '$sane=quotemeta $ENV{MATCH};s/($sane)/...

Atualizar

Veja como isso pode ser feito sem export :

MATCH=/dev/
printf "aaa\n/dev/aaa\nccc\n" \
 | perl -pe "\$sane=quotemeta q{$MATCH};s/(\$sane)/\e[1;31m\\e[0m/g"

Observe que seu código teve um erro. Você não pode usar uma referência anterior ( ) no padrão de substituição de um s/// , você precisa usar a variável de correspondência ( $1 ), que usei como \ aqui, para evitar o shell interpolando $1 por causa das aspas suaves.

Em geral, se você está fazendo muito trabalho de colorização com Perl, sugiro usar um módulo para lidar com idiossincrasias de terminal para você. Confira Termo :: ANSIColor por exemplo.

    
por 25.05.2014 / 23:53