De quais personagens eu preciso escapar ao usar sed em um script sh?

215

Pegue o seguinte script:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s//' [some file]

Se eu tentar executar isso em sh ( dash here), ele falhará por causa dos parênteses, que precisam ser escapados. Mas eu não preciso escapar das barras invertidas (entre os octetos, ou em \s ou ). Qual é a regra aqui? E quando eu preciso usar {...} ou [...] ? Existe uma lista do que eu faço e não preciso escapar?

    
por detly 28.02.2012 / 05:42

3 respostas

243

Existem dois níveis de interpretação aqui: o shell e sed.

No shell, tudo entre aspas simples é interpretado literalmente, exceto pelas própriaspas simples. Você pode efetivamente ter uma única aspa entre aspas simples escrevendo '\'' (feche aspas simples, uma aspa simples literal, abra aspas simples).

Sed usa expressões regulares básicas . Em um BRE, para que eles sejam tratados literalmente, os caracteres $.*[\]^ precisam ser citados precedendo-os por uma barra invertida, exceto dentro de conjuntos de caracteres ( […] ). Letras, dígitos e (){}+?| não devem ser citados (você pode usar algumas dessas citações em algumas implementações). As sequências \( , \) , \n e, em algumas implementações, \{ , \} , \+ , \? , \| e outras barras invertidas + alfanuméricos têm significados especiais. Você pode sair sem citar $^] em algumas posições em algumas implementações.

Além disso, você precisa de uma barra invertida antes de / se ela aparecer na expressão regular fora das expressões de colchetes. Você pode escolher um caractere alternativo como delimitador, por exemplo, s~/dir~/replacement~ ou \~/dir~p ; você precisará de uma barra invertida antes do delimitador, se quiser incluí-lo no BRE. Se você escolher um personagem que tenha um significado especial em um BRE e quiser incluí-lo literalmente, precisará de três barras invertidas; Eu não recomendo isso, pois pode se comportar de maneira diferente em algumas implementações.

Em poucas palavras, para sed 's/…/…/' :

  • Escreva o regex entre aspas simples.
  • Use '\'' para terminar com uma única citação na regex.
  • Coloque uma barra invertida antes de $.*/[\]^ e apenas esses caracteres (mas não dentro de expressões de colchetes).
  • Dentro de uma expressão de colchete, para que - seja tratado literalmente, verifique se é a primeira ou a última vez ( [abc-] ou [-abc] , não [a-bc] ).
  • Dentro de uma expressão de colchetes, para que ^ seja tratado literalmente, certifique-se de que não seja primeiro (use [abc^] , não [^abc] ).
  • Para incluir ] na lista de caracteres correspondidos por uma expressão de colchetes, escolha o primeiro caractere (ou primeiro depois de ^ para um conjunto negado): []abc] ou [^]abc] (não [abc]] nem [abc\]] ).

No texto de substituição:

  • & e \ precisam ser citados precedendo-os por uma barra invertida, assim como o delimitador (geralmente / ) e novas linhas.
  • \ seguido por um dígito tem um significado especial. \ seguido por uma letra tem um significado especial (caracteres especiais) em algumas implementações, e \ seguido por algum outro caractere significa \c ou c dependendo da implementação.
  • Com aspas simples ao redor do argumento ( sed 's/…/…/' ), use '\'' para colocar uma única cotação no texto de substituição.

Se o texto de regex ou de substituição vier de uma variável de shell, lembre-se de que

  • O regex é um BRE, não uma string literal.
  • Na regex, uma nova linha precisa ser expressa como \n (que nunca será igual a menos que você tenha outro código sed adicionando caracteres de nova linha ao espaço padrão). Mas observe que isso não funcionará dentro de expressões de colchetes com algumas sed implementações.
  • No texto de substituição, & , \ e novas linhas precisam ser citados.
  • O delimitador precisa ser citado (mas não dentro de expressões de colchetes).
  • Use aspas duplas para interpolação: sed -e "s/$BRE/$REPL/" .
por 29.02.2012 / 02:06
41

O problema que você está enfrentando não se deve à interpolação e escape do shell - é porque você está tentando usar a sintaxe de expressão regular estendida sem passar o sed a opção -r ou --regexp-extended .

Altere sua linha sed de

sed 's/(127\.0\.1\.1)\s//' [some file]

para

sed -r 's/(127\.0\.1\.1)\s//' [some file]

e funcionará como acredito que você pretenda.

Por padrão, o sed usa expressões regulares básicas (pense no estilo grep), o que exigiria a seguinte sintaxe:

sed 's/\(127\.0\.1\.1\)[ \t]//' [some file]
    
por 29.02.2012 / 03:56
14

A menos que você queira interpolar uma variável de shell na expressão sed, use aspas simples para toda a expressão, pois elas fazem com que tudo entre elas seja interpretado como está, incluindo barras invertidas.

Então, se você quiser que o sed veja s/\(127\.0\.1\.1\)\s// colocar aspas simples em torno dele e o shell não tocará os parênteses ou barras invertidas nele. Se você precisar interpolar uma variável shell, coloque apenas essa parte entre aspas duplas. Por exemplo,

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

Isso evita que você se lembre de quais metacaracteres de shell não são escapados por aspas duplas.

    
por 28.02.2012 / 06:58