¿Cómo se guarda una expresión regular compleja para la reutilización múltiple en sed?

12

Al usarlo sed, a menudo creo expresiones regulares bastante complicadas e intrincadas que necesito hacer coincidir dos veces en un archivo. ¿Hay alguna manera de guardar esta expresión regular y hacer referencia a ella dos veces?

Tal vez algo que se parece a esto?

sed ' complicated_regex=/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+$/
s/complicated_regex:complicated_regex/simple-output/
' my_file

Actualización: una respuesta ha presentado la solución de usar una variable bash. Esto no funciona Dado a test.txt.

#test.txt
foo bar
bar foo

Y el guion

#!/bin/bash

VALUE='foo \([a-z]\+\)'

sed 's/"${VALUE}"/foo happy \1/' test.txt

Esto debería producir la salida

foo happy bar
bar foo

Pero en cambio obtengo el error

sed: -e expression #1, char 24: invalid reference \1 on `s' command's RHS
Cory Klein
fuente
Si usa Perl, puede hacer referencia a partes anteriores de la expresión dentro de la expresión:perl -pe 's/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+:\1$/simple_output/' my_file
glenn jackman
1
Cuando encuentre algo demasiado complicado para sed, no dude en ir por awk o perl.
Gilles 'SO- deja de ser malvado'
2
@Cory: su ejemplo funciona bien, si obtiene su sintaxis correcta. Usted tiene la "variable $" aún encerrada en 'comillas simples' que lo oculta de la expansión de shell que espera ... debería sersed 's/'"${VALUE}"'/foo happy \1/' test.txt
Peter.O

Respuestas:

7

Puede usar variables de shell:

complicated_regex='(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+'
sed s/^"$complicated_regex":"$complicated_regex"\$/'simple-output'/ my_file

No estoy seguro de lo que quisiste decir $i, pero es posible que necesites ponerlo fuera de las comillas simples:

complicated_regex='(([a-f0-9]{32})+([a-zA-Z0-9=]{{'"$i"'}})?)+'
Stéphane Gimenez
fuente
Por supuesto, esto funciona solo si el sedcomando se invoca desde el shell, pero hay una solución similar con casi todos los lenguajes de programación. (Y no creo que sea posible usar variables dentro sed).
Stéphane Gimenez
Hrm. Al intentar esto, las referencias inversas parecen estar rotas. s/$complicated_regex/\1/da un error que dice que es una referencia inválida.
Cory Klein
Ah, tal vez es mi culpa, estoy acostumbrado a las sustituciones de variables zsh. Ver respuesta actualizada.
Stéphane Gimenez
Tendrá que eliminar los anclajes de la variable y colocarlos en el script sed:sed "s/^${complicated_regex}:${complicated_regex}\$/simple-output/" my_file
glenn jackman
Duh! Sí, olvidé comprobar que se me proporcionó una concatenación de expresiones regulares válida :-)
Stéphane Gimenez
0

La forma más fácil de colocar un valor de variable de shell sedy no preocuparse de cómo tendrá que cambiar su escape de barra invertida para el resto de su sedsecuencia de comandos, es poner todo en comillas simples excepto la variable, y ponerlo entre comillas dobles.

Todos los siguientes ejemplos de código asumen: VALUE='foo \([a-z]\+\)'

El siguiente código roto falla porque la variable VALUEno se expande:

sed 's/"${VALUE}"/foo happy \1/' test.txt

El siguiente código roto falla porque la barra invertida \1se come por el shell (porque está entre comillas dobles en lugar de comillas simples) antes de sedverlo:

sed "s/${VALUE}/foo happy \1/" test.txt

El siguiente código funciona como se esperaba:

sed 's/'"${VALUE}"'/foo happy \1/' test.txt

El siguiente código también funciona:

sed "s/${VALUE}/foo happy \\1/" test.txt

También lo siguiente:

sed s/"${VALUE}"/foo\ happy\ \\1/ test.txt

¿Pero por qué complicarse? Las comillas simples alrededor de un sedscript hacen que todo sea mucho más claro, especialmente para los gurús que no utilizan shell que leen su código. Mi forma preferida es, una vez más, abandonar las comillas simples entre comillas dobles solo para la expansión variable y volver directamente a las comillas simples:

sed 's/'"${VALUE}"'/foo happy \1/' test.txt
Comodín
fuente