Número de barras invertidas necessárias para escapar da barra invertida na linha de comando

10

Recentemente tive problemas com algum regex na linha de comando e descobriram que, para combinar uma barra invertida, diferentes números de caracteres podem ser usados. Este número depende da cotação usada para o regex (nenhum, aspas simples, aspas duplas). Veja o seguinte sessão bash para o que quero dizer:

echo "#ab\cd" > file
grep -E ab\cd file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\cd file
#ab\cd
grep -E ab\\\cd file
#ab\cd
grep -E ab\\\cd file
#ab\cd
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\cd file
grep -E "ab\cd" file
grep -E "ab\cd" file
grep -E "ab\\cd" file
#ab\cd
grep -E "ab\\cd" file
#ab\cd
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\cd' file
#ab\cd
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\cd' file

Isso significa que:

  • sem aspas, posso fazer a correspondência de uma barra invertida com 4-7 barras invertidas reais
  • com aspas duplas, posso combinar uma barra invertida com 3-6 barras invertidas reais
  • Com aspas simples, posso corresponder uma barra invertida com 2-3 barras invertidas reais

Eu entendo que uma barra invertida extra é ignorada pelo shell (de a página man bash):

"Uma barra invertida sem aspas (\) é o caractere de escape. Ele preserva  o valor literal do próximo caractere que segue "

Isso não se aplica aos exemplos de citações simples, porque não O escape é feito entre aspas simples.

E uma contrabarra adicional é ignorada pelo comando grep ("\ c" é apenas "c" escapou, mas isso é exatamente o mesmo que "c", porque "c" não tem um significado especial em uma regex).

Isso explica o comportamento do exemplo com aspas simples, mas Eu realmente não entendo os outros dois exemplos, especialmente porque há uma diferença entre sequências não-qouted de aspas duplas.

Mais uma vez, uma citação da página man bash:

"Incluir caracteres entre aspas duplas preserva o valor literal de todos os caracteres entre aspas, com exceção de $, ', \, e, quando a expansão do histórico estiver ativada,!. "

Eu tentei o mesmo com o GNU awk (por exemplo, awk /ab\cd/{print} file ), com os mesmos resultados.

Perl, no entanto, mostra resultados diferentes (usando, por exemplo, perl -ne "/ab\cd/"\&\&print file ):

  • sem aspas, posso fazer a correspondência de uma barra invertida com 4-5 barras invertidas reais
  • com aspas duplas, posso combinar uma barra invertida com 3-4 barras invertidas reais
  • Com aspas simples, posso fazer a correspondência de uma barra invertida com duas barras invertidas reais

Alguém pode explicar essa diferença entre não citado e double-qouted strings regex na linha de comando para grep e awk? Eu não estou tão interessado em uma explicação do comportamento do Perl, já que eu não costumo usar o one-liner do Perl.

    
por daniel kullmann 21.02.2012 / 12:53

2 respostas

9

Para o exemplo não indicado, cada par de \ passa uma barra invertida para o grep, então 4 barras invertidas passam duas para o grep, o que se traduz em uma única barra invertida. 6 barras invertidas passam três para grep, traduzindo para uma barra invertida e uma \c , que é igual a c . Uma barra invertida adicional não altera nada, porque está traduzida em \c - > c pelo shell. Oito barras invertidas no shell são quatro no grep, traduzidas para dois, então isso não combina mais.

Para o exemplo entre aspas duplas, observe o que segue sua segunda citação na manchage bash:

The backslash retains its special meaning only when followed by one of the following characters: $, ', ", \, or newline.

Ou seja. quando você dá um número ímpar de barras invertidas, a sequência termina em \c , o que seria igual a c no caso não mencionado, mas quando citada, a barra invertida perde seu significado especial, portanto \c é passado para grep. É por isso que o intervalo de barras invertidas "possíveis" (ou seja, aquelas que compõem um padrão correspondente ao seu arquivo de exemplo) desliza para baixo em um.

    
por 21.02.2012 / 14:08
5

Este link descreve o bash Citações e Escapando

Sua pergunta lida com as três primeiras seções.

  • escape por caractere
  • Weak quoting "aspas duplas"
  • Citações strongs 'aspas simples'
  • ANSI C como a citação de strings
  • I18N / L10N citando (internacionalização e localização) .

Abaixo está um gráfico de como as strings como bash as transmitem para grep e como grep as interpreta internamente.

Vamos primeiro olhar para echo "#ab\cd" > file .
No fraco-citado ("") "#ab\cd" , o \ é um escape \ que é passado para file como um único literal \ . Então, file contém ab\cd

Agora, para seus comandos: O gráfico abaixo pode ajudar a ver o que realmente acontece com cada chamada. O * mostra os que correspondem ao conteúdo do arquivo. É realmente apenas uma questão de aplicar as regras de escape do bash, como na página web, com uma nota especial para a resposta do daniel kullmann onde ele se refere ao comportamento de escape em um quoting fraco situação.

The backslash retains its special meaning only when followed by one of the following characters: $, ', ", \, or newline.

                            bash passes    grep further
                            to grep        resolves to         
grep -E ab\cd file            abcd           abcd   
grep -E ab\cd file           ab\cd          abcd  
grep -E ab\\cd file          ab\cd          abcd
grep -E ab\\cd file         ab\cd         ab\cd    * 
grep -E ab\\\cd file        ab\\cd        ab\cd    *
grep -E ab\\\cd file       ab\\cd        ab\cd    *    
grep -E ab\\\\cd file      ab\\cd        ab\cd    *
grep -E ab\\\\cd file     ab\\cd       ab\cd

grep -E "ab\cd" file          ab\cd          abcd
grep -E "ab\cd" file         ab\cd          abcd
grep -E "ab\\cd" file        ab\cd         ab\cd    *
grep -E "ab\\cd" file       ab\cd         ab\cd    *
grep -E "ab\\\cd" file      ab\\cd        ab\cd    *
grep -E "ab\\\cd" file     ab\\cd        ab\cd    *
grep -E "ab\\\\cd" file    ab\\cd       ab\cd    

grep -E 'ab\cd' file          ab\cd          abcd  
grep -E 'ab\cd' file         ab\cd         ab\cd    *
grep -E 'ab\\cd' file        ab\\cd        ab\cd    *
grep -E 'ab\\cd' file       ab\\cd       ab\cd
    
por 21.02.2012 / 15:18