Cómo asegurarse de que la cadena interpolada en la sustitución 'sed' escapa a todos los metacars

21

Tengo un script que lee una secuencia de texto y genera un archivo de comandos sed que luego se ejecuta sed -f. Los comandos sed generados son como:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

Suponga que el script que genera los sedcomandos es algo como:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

¿Cómo puedo mejorar el script para asegurar que todos los metacaracteres regex en la cidcadena se escapen y se interpolen correctamente?

dan
fuente

Respuestas:

24

Para escapar de las variables que se utilizarán en el lado izquierdo y en el lado derecho de un scomando en sed(aquí $lhsy $rhsrespectivamente), debe hacer lo siguiente:

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')

sed "s/$escaped_lhs/$escaped_rhs/"

Tenga en cuenta que $lhsno puede contener un carácter de nueva línea.

Es decir, en el LHS, escapa de todos los operadores regexp ( ][.^$*), el propio carácter de escape ( \) y el separador ( /).

En el RHS, solo necesita escapar &, el separador, la barra diagonal inversa y el carácter de nueva línea (lo que hace insertando una barra diagonal inversa al final de cada línea excepto la última ( $!s/$/\\/)).

Eso supone que lo usa /como separador en sus sed scomandos y que no habilita los RE extendidos con -r(GNU sed/ ssed/ ast/ busybox sed) o -E(BSD, astGNU reciente, busybox reciente) o PCRE con -R( ssed) o RE aumentados con -A/ -X( ast) que todos tienen operadores RE adicionales.

Algunas reglas básicas cuando se trata de datos arbitrarios:

  • No usar echo
  • cita tus variables
  • considere el impacto de la configuración regional (especialmente su conjunto de caracteres: es importante que los comandos de escape sed se ejecuten en la misma configuración regional que el sedcomando usando las cadenas de escape (y con el mismo sedcomando) por ejemplo)
  • no se olvide del carácter de nueva línea (aquí es posible que desee verificar si $lhscontiene alguno y tomar medidas).

Otra opción es usar en perllugar de sedy pasar las cadenas en el entorno y usar los operadores \Q/ \E perlregexp para tomar cadenas literalmente:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl(por defecto) no se verá afectado por el conjunto de caracteres de la configuración regional ya que, en lo anterior, solo considera las cadenas como matrices de bytes sin preocuparse por qué caracteres (si los hay) pueden representar para el usuario. Con sed, puede lograr lo mismo arreglando la configuración regional Ccon LC_ALL=Cpara todos los sedcomandos (aunque eso también afectará el idioma de los mensajes de error, si los hay).

Stéphane Chazelas
fuente
¿Qué pasa si necesito escapar de las comillas dobles?
Menon
@Menon, las comillas dobles no son especiales sed, no necesitas escapar de ellas.
Stéphane Chazelas
Esto no se puede usar para la coincidencia de patrones con comodines, ¿verdad?
Menon
@Menon, no, la coincidencia de patrones comodín como con find's -namees diferente de las expresiones regulares. Allí solo necesita escapar ?, *barra invertida y[
Stéphane Chazelas