Expressão regular usando \\ vs using \

10

Por que

grep e\.g\. <<< "this is an e.g. wow"

e

grep e\.g\. <<< "this is an e.g. wow"

faz a mesma coisa?

Se eu adicionar uma terceira barra, ela também terá o mesmo resultado. MAS, uma vez que eu adicionei uma quarta barra, ela não funciona mais. Isso tem a ver com uma pergunta de um exame antigo para uma turma. Perguntou se aquele com duas barras invertidas se trabalharia para produzir a linha com "por exemplo" Eu originalmente pensei que não funcionaria, mas eu tentei me certificar e o fiz. Qual é a explicação?

    
por Wyatt Grant 20.10.2014 / 01:14

4 respostas

9

Primeiro, observe que a barra simples corresponde muito:

$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
 e.g.

No que diz respeito a Bash , um período de escape é o mesmo que um período. Bash passa o período para grep . Para grep, um período corresponde a qualquer coisa.

Agora, considere:

$ echo $'eegg \n e.g.' | grep e\.g\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\.g\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\.g\\.
$

Quando o Bash vê uma barra dupla, reduz-a a uma única barra e a passa para o grep que, no primeiro dos três testes acima, vê, como queremos, uma única barra antes de um ponto. Assim, isso faz a coisa certa.

Com uma barra tripla, o Bash reduz os dois primeiros para uma única barra. Em seguida, ele vê \. . Como um período de escape não tem um significado especial para o Bash, isso é reduzido a um período simples. O resultado é que o grep vê, como queremos, uma barra antes de um período.

Com quatro barras, o Bash reduz cada par a uma única barra. Bash passa para grep duas barras e um ponto final. O grep vê as duas barras e um ponto e reduz as duas barras a uma única barra de texto literal . A menos que a entrada tenha uma barra literal seguida por qualquer caractere, não há correspondências.

Para ilustrar isso por último, lembre-se de que dentro de aspas simples, todos os caracteres são literais. Assim, dadas as três linhas de entrada a seguir, o comando grep corresponde apenas na linha com a barra literal na entrada:

$ echo 'eegg
e.g.
e\.g\.' |  grep e\\.g\\.
e\.g\.

Resumo do comportamento de Bash

Para Bash, as regras são

  • Duas barras são reduzidas a uma única barra.

  • Uma barra na frente de um caractere normal, como um ponto, é apenas o caractere normal (ponto).

Assim:

$ echo \. \. \\. \\.
. \. \. \.

Existe uma maneira simples de evitar toda essa confusão: na linha de comando do Bash, expressões regulares devem ser colocadas entre aspas simples. Dentro daspas simples, Bash deixa tudo em paz.

$ echo '\. \. \\. \\.'  # Note single-quotes
\. \. \\. \\.
    
por 20.10.2014 / 02:01
4

A saída é a mesma apenas para sua string, mas em geral essas expressões regulares fazem coisas diferentes. Vamos modificar um pouco seu exemplo adicionando o segundo padrão e,g, (com comas), terceiro e\.g\. (pontos), quarto e\,g\, (comas) e -o opção para grep para imprimir apenas as partes correspondentes.

  • No seguinte caso, . corresponde a qualquer caractere (observe '' em torno de e.g. , eu chegarei a isso mais tarde)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
    
  • Em seguida, escapamos de . com barra invertida \ , então somente literal . será correspondido:

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    
  • Mas podemos escapar de \ com outro \ , de modo que o literal \ seja correspondido seguido por . (ou seja, qualquer caractere):

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
    
  • Mas se quisermos corresponder apenas a \. not \, , ainda será necessário outro \ para escapar do significado especial do ponto:

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    

Agora, como você não usou o argumento '' around grep, é necessário adicionar outras barras invertidas para escapar das barras invertidas da interpretação do shell, portanto:

grep 'e\.g\.'     => grep e\.g\.
grep 'e\.g\.'   => grep e\\.g\\.  (each backslash has to be quoted separately)
grep 'e\\.g\\.' => grep e\\\.g\\\. (3 x 2 = 6 backslashes in total)
    
por 20.10.2014 / 02:06
3

Quando você faz um grep e\.g\. , o shell está consumindo a barra invertida, portanto, você está fazendo um grep e.g. , que corresponde. Quando você faz um grep e\.g\. , o shell está novamente consumindo uma barra, e agora você está fazendo um grep e\.\g. , que corresponde novamente. Agora, uma barra invertida no shell se parece com \ . Então, quando você tem \ , o primeiro é uma seqüência de escape, o segundo é uma barra invertida literal. Quando você faz um grep e\\.g\\. , ele ainda acaba sendo grep e\.\g. , porque não há uma seqüência de escape ( \ ) antes do primeiro \ para torná-lo literal \ . Lembre-se \ é uma barra invertida, portanto grep e\\.\\g acaba sendo grep e\.g\. , o que obviamente não combina.

Para ver como o shell está vendo o que você está fazendo, use echo (por exemplo, echo grep e\.g\. <<< "this is an e.g. wow" vs. echo grep e\\.g\\. <<< "this is an e.g. wow" )

    
por 20.10.2014 / 02:05
0

Os dois comandos produzem a mesma saída apenas para sua entrada, mas de outra forma eles são diferentes. Para entender o que está acontecendo, precisamos saber como o parâmetro é interpretado primeiro por bash e depois por grep .

Escapando no bash

\ é um caractere especial que cancela o significado especial do caractere seguinte, incluindo \ em si. Se o seguinte caractere não tiver um significado especial, ele será passado sem alteração. Exemplos com comando e um resultado:

  • echo \a : a - personagem ordinário que escapou dá o caractere
  • echo \ : \ - caractere especial escapado dá o caractere
  • echo \\a : \a - combinação especial, comum
  • echo \\ : \ - combinação especial, especial

echo imprimirá a string resultante depois que bash a interpretar. Mais informações: documentação do bash , bash hackers wiki , Especificação POSIX .

. não tem significado especial em bash . É um caractere comum para o shell. Abaixo estão as sequências relevantes para seus exemplos:

  • echo . : .
  • echo \. : .
  • echo \. : \.
  • echo \\. : \.
  • echo \\. : \.

Solução mais simples para strings literais no bash

Para passar parâmetros literalmente por bash , você pode usar aspas simples ' escape. Entre aspas simples, você não precisa se importar com o significado especial dos caracteres, porque a aspa simples é o único caractere com um significado especial. Você pode inserir uma aspa simples depois de incluir a primeira parte da string. Exemplo: echo 'part1'\''part2' : part1'part2

Regex no grep

\ é um caractere de escape com significado semelhante ao de bash . . é um caractere especial que representa uma única ocorrência de qualquer caractere . Veja: regex POSIX , GNU grep regex . Exemplos de expressões regex:

  • . - corresponde a qualquer caractere como a ou .
  • \. - corresponde apenas a . literalmente

Seus exemplos

Na segunda linha de cada exemplo abaixo, você encontrará equivalentes com aspas simples ' mostrando qual string literal é passada por bash to grep . Então, depois que grep executar o escape, o único caractere especial possível nos exemplos será . correspondente a qualquer caractere. Na terceira linha, há uma descrição do que a expressão corresponde.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    e qualquer caractere g qualquer caractere - corresponde a e.g. e possivelmente outras strings como eagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    e qualquer caractere g qualquer caractere - corresponde a e.g. e possivelmente outras strings como exgy
  • grep e\.g\. <<< "this is an e.g. wow" em grep 'e\.g\.' <<< "this is an e.g. wow" em e.g. literalmente - corresponde apenas a e.g.
  • grep e\\.g\\. <<< "this is an e.g. wow" em grep 'e\.g\.' <<< "this is an e.g. wow" em e.g. literalmente - corresponde apenas a e.g.
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e\ qualquer caractere g\ qualquer caractere - não corresponde a e.g.
por 20.10.2014 / 12:24