Quais caracteres eu preciso escapar ao usar o sed em um script sh?

248

Pegue o seguinte script:

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

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

detly
fonte
1
Aqui é uma função do bash para converter caminhos para uso com SED:function sedPath { path=$((echo $1|sed -r 's/([\$\.\*\/\[\\^])/\\\1/g'|sed 's/[]]/\[]]/g')>&1) } #Escape path for use with sed
user2428118
Dura lex, sed sed
Nemo

Respostas:

282

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

No shell, tudo entre aspas simples é interpretado literalmente, exceto as próprias aspas. Você pode efetivamente ter uma única citação entre aspas simples, escrevendo '\''(aspas simples fechadas, uma aspas simples literal, aspas simples abertas).

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 dos conjuntos de caracteres ( […]). Letras, dígitos e (){}+?|não devem ser citados (você pode citar alguns deles em algumas implementações). As sequências \(, \), \n, e em algumas implementações \{, \}, \+, \?, \|e outra barra invertida + alfanuméricos têm significados especiais. Você pode evitar não citar $^em algumas posições em algumas implementações.

Além disso, você precisa de uma barra invertida antes /para aparecer na regex fora das expressões entre colchetes. Você pode escolher um caractere alternativo como delimitador escrevendo, por exemplo, s~/dir~/replacement~ou \~/dir~p; você precisará de uma barra invertida antes do delimitador, se desejar incluí-lo no BRE. Se você escolher um personagem que tenha um significado especial em um BRE e desejar 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 a regex entre aspas simples.
  • Use '\''para terminar com uma citação única na regex.
  • Coloque uma barra invertida antes $.*/[\]^e somente esses caracteres (mas não dentro das expressões entre colchetes). (Tecnicamente, você não deve colocar uma barra invertida antes, ]mas eu não conheço uma implementação que trate ]e \]diferentemente das expressões entre colchetes.)
  • Dentro de uma expressão entre colchetes, para -ser tratada literalmente, verifique se é a primeira ou a última ( [abc-]ou [-abc]não [a-bc]).
  • Dentro de uma expressão agrupada, para ^ser tratado literalmente, ter certeza que é não primeiro (uso [abc^], não [^abc]).
  • Para incluir ]na lista de caracteres correspondidos por uma expressão entre colchetes, torne-o o primeiro caractere (ou o primeiro depois ^de 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 as 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 meio de caractere \cou cdependendo da implementação.
  • Com aspas simples ao redor do argumento ( sed 's/…/…/'), use '\''para colocar uma aspas simples no texto de substituição.

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

  • O regex é um BRE, não uma sequência literal.
  • Na regex, uma nova linha precisa ser expressa como \n(que nunca corresponderá, a menos que você tenha outro sedcódigo adicionando caracteres de nova linha ao espaço do padrão). Mas observe que ele não funcionará dentro das expressões de colchete em algumas sedimplementações.
  • No texto de substituição, &, \e novas linhas precisam ser citado.
  • O delimitador precisa ser citado (mas não dentro das expressões entre colchetes).
  • Use aspas duplas para interpolação: sed -e "s/$BRE/$REPL/".
Gilles
fonte
Escapando o caractere curinga real (*), você pode usar barra invertida dupla ( \\*). Exemplo:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
danger89 20/03
43

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 pela opção -rou --regexp-extended.

Mude sua linha sed de

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

para

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

e funcionará como acredito que você pretende.

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

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
R Perrin
fonte
Eu tive esse problema novamente e esqueci de rolar para baixo para encontrar a solução que eu votei na última vez. Obrigado novamente.
Isaaclw #
Muito obrigado. Adicionar -rcomo opção foi o necessário no meu caso.
HelloGoodbye
15

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

Portanto, se você deseja que o sed veja s/\(127\.0\.1\.1\)\s/\1/aspas simples, o shell não tocará nos parênteses ou nas barras invertidas. Se você precisar interpolar uma variável de shell, coloque apenas essa parte entre aspas duplas. Por exemplo

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

Isso evitará que você lembre quais metacaracteres do shell não são escapados por aspas duplas.

Kyle Jones
fonte
Eu quero sedver s/(127\.0\.1\.1)/..., mas colocar isso em um script de shell como está não funciona. O que você está dizendo sobre o shell não tocar nos parênteses parece errado. Eu editei minha pergunta para elaborar.
detly
3
A concha não está tocando os parênteses. Você precisa dos backslases porque o sed precisa vê-los. sed 's/(127\.0\.1\.1)/IP \1/'falha porque o sed precisa ver \(e \)para a sintaxe do grupo, não (e ).
Kyle Jones
facepalm Não está na página de manual, mas está em algum manual online que encontrei. Isso é normal para regex, porque eu nunca tive que usá-lo em bibliotecas regex (em, por exemplo, Python)?
detly
3
Para comandos tradicionais do Unix, existem expressões regulares básicas e expressões regulares estendidas. Detalhes . O sed usa expressões regulares básicas, portanto as barras invertidas são necessárias para a sintaxe do grupo. Perl e Python foram além de expressões regulares estendidas. Enquanto eu estava bisbilhotando, encontrei um gráfico extremamente informativo que ilustra o que é uma confusão confusa que evocamos quando dizemos com glamour "expressão regular".
Kyle Jones
1
Eu também acrescentaria que o único caractere que não pode ser usado dentro de aspas simples é uma aspas simples.
enzotib