Cómo pasar una variable que contiene barras a sed

100

¿Cómo se pasa una variable que contiene barras como patrón sed?

Por ejemplo, si tengo la siguiente variable:

var="/Users/Documents/name/file"

Quiero pasarlo a sedcomo tal:

sed "s/$var/replace/g" "$file"

Sin embargo, obtengo errores. ¿Cómo puedo eludir el problema?

buydadip
fuente

Respuestas:

193

Use un delimitador de expresiones regulares alternativo, ya sedque le permite usar cualquier delimitador ( incluidos los caracteres de control ):

sed "s~$var~replace~g" $file
anubhava
fuente
2
No funciona. Intento sed -i "s ~ blah ~ $ var ~ g file" y obtengo: "sed -e expression # 1, char 70: unterminated` s 'command ". Confirmé que el culpable es una barra en $ var. Las variantes de esta solución a esto están por todas partes y ninguna de ellas funciona. ¿Se actualizó sed recientemente? Estoy en la v4.2.2 en Ubuntu 14.04.4 LTS.
Paul Ericson
Eso depende del valor de$var
anubhava
1
No funciona con ~ como observó @PaulEricson, pero aún funciona con |
Kenny Ho
1
El último "~" (después de g) parece no ser necesario, al menos para AWS Amazon Linux (basado en CentOS)
Saúl Martínez Vidals
1
Me salvaste el día
user7131571
61

Una respuesta pura de bash: use la expansión de parámetros para la barra invertida y escape de cualquier barra en la variable:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file
Glenn Jackman
fuente
1
Esta es una buena forma, porque no siempre sabes qué caracteres contiene la variable. Por lo tanto, siempre puede escapar del delimitador sed si hay una duda. Por ejemplo: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L
Hermoso. Hice algunos scripts de cambio de nombre mucho más limpios.
Cloud
Aprendí algo hermoso hoy, gracias. Debería ser la respuesta aceptada.
Steve Kehlet
6
Un poco de claridad sobre el ser patrón utilizado aquí: ${parameter/pattern/string}. Entonces, en este caso, el parámetro es var, el patrón es /\/y la cadena es \\/. Todas las instancias del patrón se reemplazan porque el patrón comienza con a /.
Josh
1
@josh, por lo que la barra inclinada inicial del patrón no es en realidad parte del patrón a reemplazar.
Glenn Jackman
32

Otra forma de hacerlo, aunque más fea que la respuesta de anubhava, es escapando de todas las barras invertidas varusando otro sedcomando:

var=$(echo "$var" | sed 's/\//\\\//g')

entonces, esto funcionará:

sed "s/$var/replace/g" $file
buydadip
fuente
12

El uso de /in sed como delimitador entrará en conflicto con las barras en la variable cuando se sustituya y, como resultado, obtendrá un error. Una forma de eludir esto es usar otro delimitador que sea exclusivo de cualquier carácter que esté en esa variable.

var="/Users/Documents/name/file"

puede utilizar el carácter octothorpe que se adapte a la ocasión (o cualquier otro carácter que no sea /para facilitar su uso)

sed "s#$var#replace#g" 

o

sed 's#$'$var'#replace#g'

esto es adecuado cuando la variable no contiene espacios

o

sed 's#$"'$var'"#replace#g'

Es más prudente usar lo anterior, ya que estamos interesados ​​en sustituir lo que sea que esté en esa variable solo en comparación con las comillas dobles del comando completo, lo que puede hacer que su shell interpele cualquier carácter que pueda considerarse un carácter especial de shell en el shell.

repzero
fuente
No funciona. Intento sed -i "s ~ blah ~ $ var ~ g file" y obtengo: "sed -e expression # 1, char 70: unterminated` s 'command ". Confirmé que el culpable es una barra en $ var. Las variantes de esta solución a esto están por todas partes y ninguna de ellas funciona. ¿Se actualizó sed recientemente? Estoy en la v4.2.2 en Ubuntu 14.04.4 LTS.
Paul Ericson
@PaulEricson No puedo reproducir esto en ningún Ubuntu disponible para mí. Si $varcontiene ~, obviamente deberá elegir un delimitador diferente. El error "no terminado" parece que tu $varcontiene una nueva línea; es posible que pueda solucionarlo haciendo una barra invertida escapando de cada nueva línea en el valor, pero no creo que esto sea completamente portátil. En general, esto no está relacionado con el problema de las barras en el valor.
tripleee
@PaulEricson Oh, y su cita es incorrecta, no debe tener el nombre del archivo entre comillas. sed -i "s~blah~$var~g" filecon comillas solo alrededor del sedguión real .
tripleee
5

Esta es una pregunta antigua, pero ninguna de las respuestas aquí discute las operaciones más que s/from/to/con mucho detalle.

La forma general de una seddeclaración es

*address* *action*

donde la dirección puede ser un rango de expresiones regulares o un rango de número de línea (o vacío, en cuyo caso la acción se aplica a cada línea de entrada). Así por ejemplo

sed '1,4d' file

eliminará las líneas 1 a 4 (la dirección es el rango del número de línea 1,4y la acción es el dcomando de eliminación); y

sed '/ick/,$s/foo/bar/' file

reemplazará la primera aparición de foocon baren cualquier línea entre la primera coincidencia en la expresión regular icky el final del archivo (la dirección es el rango /ick/,$y la acción es el scomando de sustitución s/foo/bar/).

En este contexto, si ickproviene de una variable, podría hacer

sed "/$variable/,\$s/foo/bar/"

(observe el uso de comillas dobles en lugar de simples, de modo que el shell pueda interpolar la variable, y la necesidad de citar el signo de dólar literal entre comillas dobles) pero si la variable contiene una barra, obtendrá un error de sintaxis. (El shell expande la variable, luego pasa la cadena resultante a sed; por lo tanto, sedsolo ve texto literal, no tiene el concepto de las variables del shell).

La cura es usar un delimitador diferente (donde obviamente necesita poder usar un carácter que no puede ocurrir en el valor de la variable), pero a diferencia del s%foo%bar%caso, también necesita una barra invertida antes del delimitador si desea usar un carácter diferente delimitador que el predeterminado /:

sed "\\%$variable%,\$s/foo/bar/" file

(entre comillas simples, una barra invertida obviamente sería suficiente); o puede escapar por separado de cada barra en el valor. Esta sintaxis en particular es solo Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

o si usa un caparazón diferente, intente

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Para mayor claridad, si $variablecontiene la cadena 1/2, los comandos anteriores serían equivalentes a

sed '\%1/2%,$s/foo/bar/' file

en el primer caso, y

sed '/1\/2/,$s/foo/bar/' file

en el segundo.

triples
fuente
4

Utilice Perl, donde las variables son ciudadanos de primera clase, no solo macros en expansión:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p lee la entrada línea por línea e imprime la línea después del procesamiento
  • \Qcita todos los metacaracteres en la siguiente cadena (no es necesario para el valor presentado aquí, pero es necesario si el valor contiene [o algunos otros valores especiales para expresiones regulares)
choroba
fuente
La única respuesta generalmente útil a docenas de preguntas sobre "uso de variables en expresiones sed" en Stack Exchange. Todo lo demás es efectivamente "escapar de todo lo que sea especial a sed", lo cual es prácticamente inútil dada la variabilidad de variables y de sed.
Heath Raftery