¿Qué caracteres necesito para escapar cuando uso sed en un script sh?

248

Tome el siguiente script:

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

Si intento ejecutar esto en sh( dashaquí), fallará debido a los paréntesis, que deben escaparse. Pero no necesito escapar de las barras diagonales (entre los octetos, o en el \so \1). ¿Cuál es la regla aquí? ¿Qué pasa cuando necesito usar {...}o [...]? ¿Hay una lista de lo que hago y no necesito escapar?

desviarse
fuente
1
Aquí hay una función bash para convertir rutas para usar con 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

Respuestas:

282

Aquí hay dos niveles de interpretación: el caparazón y sed.

En el shell, todo entre comillas simples se interpreta literalmente, excepto las comillas simples. Puede escribir una cita simple entre comillas simples '\''(comilla simple cerrada, una comilla simple literal, comilla simple abierta).

Sed usa expresiones regulares básicas . En un BRE, para que sean tratados literalmente, los caracteres $.*[\^deben ser citados precediéndolos con una barra diagonal inversa, excepto dentro de los conjuntos de caracteres ( […]). Las letras, los dígitos y (){}+?|no se deben citar (puede salirse con la cita de algunos de estos en algunas implementaciones). Las secuencias \(, \), \n, y, en algunas implementaciones \{, \}, \+, \?, \|y otra barra invertida + alfanuméricos tienen significados especiales. Puede salirse con la suya sin cotizar $^en algunas posiciones en algunas implementaciones.

Además, necesita una barra invertida antes /si va a aparecer en la expresión regular fuera de las expresiones de paréntesis. Puede elegir un carácter alternativo como delimitador escribiendo, por ejemplo, s~/dir~/replacement~o \~/dir~p; necesitará una barra invertida antes del delimitador si desea incluirlo en el BRE. Si elige un personaje que tiene un significado especial en un BRE y desea incluirlo literalmente, necesitará tres barras invertidas; No recomiendo esto, ya que puede comportarse de manera diferente en algunas implementaciones.

En pocas palabras, para sed 's/…/…/':

  • Escribe la expresión regular entre comillas simples.
  • Use '\''para terminar con una comilla simple en la expresión regular.
  • Ponga una barra diagonal inversa antes $.*/[\]^y solo esos caracteres (pero no dentro de las expresiones entre corchetes). (Técnicamente, no deberías poner una barra invertida antes, ]pero no conozco una implementación que trate ]y de manera \]diferente fuera de las expresiones de paréntesis).
  • Dentro de una expresión de paréntesis, para -que se trate literalmente, asegúrese de que sea el primero o el último ( [abc-]o [-abc]no [a-bc]).
  • Dentro de una expresión de paréntesis, para ^que se trate literalmente, asegúrese de que no sea primero (use [abc^], not [^abc]).
  • Para incluir ]en la lista de caracteres que coinciden con una expresión de paréntesis, conviértalo en el primer carácter (o el primero después ^de un conjunto negado): []abc]o [^]abc](no [abc]]ni[abc\]] ).

En el texto de reemplazo:

  • &y \deben ser citados precediéndolos con una barra diagonal inversa, al igual que el delimitador (generalmente /) y las nuevas líneas.
  • \seguido de un dígito tiene un significado especial. \seguido de una letra tiene un significado especial (caracteres especiales) en algunas implementaciones, y \seguido de algún otro medio de caracteres \co cdependiendo de la implementación.
  • Con comillas simples alrededor del argumento ( sed 's/…/…/'), use '\''para poner una comilla simple en el texto de reemplazo.

Si la expresión regular o el texto de reemplazo proviene de una variable de shell, recuerde que

  • La expresión regular es un BRE, no una cadena literal.
  • En la expresión regular, una nueva línea debe expresarse como \n(que nunca coincidirá a menos que tenga otro sedcódigo que agregue caracteres de nueva línea al espacio del patrón). Pero tenga en cuenta que no funcionará dentro de las expresiones de paréntesis con algunas sedimplementaciones.
  • En el texto de reemplazo, &, \y saltos de línea deben ser citado.
  • El delimitador necesita ser citado (pero no dentro de las expresiones de paréntesis).
  • Utilizar comillas dobles para la interpolación: sed -e "s/$BRE/$REPL/".
Gilles
fuente
Para escapar del carácter comodín real (*), puede usar una barra diagonal inversa doble ( \\*). Ejemplo:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
danger89
43

El problema que está experimentando no se debe a la interpolación y los escapes de shell, sino a que está intentando utilizar la sintaxis de expresión regular extendida sin pasar la opción -ro --regexp-extended.

Cambia tu línea sed de

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

a

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

y funcionará como creo que pretendes.

De manera predeterminada, sed uses utiliza expresiones regulares básicas (piense en el estilo grep), lo que requeriría la siguiente sintaxis:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
R Perrin
fuente
Tuve este problema nuevamente y olvidé desplazarme hacia abajo para encontrar la solución que voté la última vez. Gracias de nuevo.
isaaclw
Muchas gracias. Agregar -rcomo opción era lo que era necesario en mi caso.
Hola
15

A menos que desee interpolar una variable de shell en la expresión sed, use comillas simples para toda la expresión porque hacen que todo entre ellas se interprete como está, incluidas las barras diagonales inversas.

Por lo tanto, si desea ver, s/\(127\.0\.1\.1\)\s/\1/coloque comillas simples alrededor y el shell no tocará los paréntesis o las barras invertidas. Si necesita interpolar una variable de shell, ponga solo esa parte entre comillas dobles. P.ej

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

Esto le ahorrará la molestia de recordar qué metacaracteres de shell no se escapan con comillas dobles.

Kyle Jones
fuente
Quiero sedver s/(127\.0\.1\.1)/..., pero poner eso en un script de shell como está no funciona. Lo que estás diciendo acerca de que el caparazón no toca los paréntesis parece incorrecto. He editado mi pregunta para elaborar.
desviarse
3
El caparazón no toca los paréntesis. Necesita las barras diagonales inversas porque sed necesita verlas. sed 's/(127\.0\.1\.1)/IP \1/'falla porque sed necesita ver \(y \)para la sintaxis de grupo, no (y ).
Kyle Jones
facepalm No está en la página del manual, pero sí en algún manual en línea que encontré. ¿Es esto normal para regex, porque nunca he tenido que usarlo en bibliotecas de expresiones regulares (en, por ejemplo, Python)?
desvío
3
Para los comandos tradicionales de Unix, hay expresiones regulares básicas y expresiones regulares extendidas. Detalles . sed usa expresiones regulares básicas, por lo que las barras invertidas son necesarias para la sintaxis de grupo. Perl y Python fueron más allá incluso de expresiones regulares extendidas. Mientras estaba hurgando, encontré un cuadro extremadamente informativo que ilustra la zarza confusa que evocamos cuando decimos "expresión regular".
Kyle Jones
1
También agregaría que el único carácter que no se puede usar dentro de comillas simples es una comilla simple.
enzotib