2012-01-29 14 views
17

¿Cuál es la sintaxis correcta para encontrar una subcadena (una cadena que va precedida y seguida por cadenas específicas) que hace que no concuerde con un patrón específico?Sed regex y subcadena de negación

Por ejemplo, yo quiero tomar todas las subcadenas, que empiezan con BEGIN_, final con _END y la subcadena en el medio es no igual a FOO; y reemplace toda la subcadena con el formato "(subcadena interna)". A continuación se correspondería con:

  • BEGIN_bar_END ->(bar)
  • BEGIN_buz_END ->(buz)
  • BEGIN_ihfd8f398IHFf9f39_END ->(ihfd8f398IHFf9f39)

Pero BEGIN_FOO_END no va a coincidir.

He jugado un poco con lo siguiente, pero parece que no puede encontrar la sintaxis correcta:

sed -e 's/BEGIN_(^FOO)_END/($1)/g' 
sed -e 's/BEGIN_([^FOO])_END/($1)/g' 
sed -e 's/BEGIN_(?!FOO)_END/($1)/g' 
sed -e 's/BEGIN_(!FOO)_END/($1)/g' 
sed -e 's/BEGIN_(FOO)!_END/($1)/g' 
sed -e 's/BEGIN_!(FOO)_END/($1)/g' 
+0

Como nota, cuando se trata de líneas completas, esto se puede lograr usando '': http://www.grymoire.com/Unix/Sed.html#uh-32 – Zenexer

Respuesta

27

no hay ningún operador general de la negación en Sed, IIRC porque compilación de expresiones regulares con la negación a DFA toma tiempo exponencial. Puede solucionar esto con

'/BEGIN_FOO_END/b; s/BEGIN_\(.*\)_END/(\1)/g' 

donde /BEGIN_FOO_END/b significa: si encontramos BEGIN_FOO_END, entonces rama (salto) al final de la secuencia de comandos sed.

+9

también se podría escribir 'sed '/ BEGIN_FOO_END /! S/BEGIN _ \ (. * \) _ END/(\ 1)/g'' – potong

+2

Me gustaría señalar que' sed'/BEGIN_FOO_END /! S | BEGIN_ \ (.* \) _ END | (\ 1) | g'' funciona pero 'sed '| BEGIN_FOO_END |! S | BEGIN _ \ (. * \) _ END | (\ 1) | g'' no! Evidentemente, le permite sustituir un separador diferente de "/" en la última sección, pero no en la primera sección. Extraño. – CommaToast

+1

@CommaToast El comando 's ///' puede usar un delimitador arbitrario; las direcciones no pueden. – TheDudeAbides

2

No sé de una manera bastante, pero siempre se podía hacer esto:

$ cat file 
BEGIN_FOO_END 
BEGIN_FrOO_END 
BEGIN_rFOO_END 
$ sed '/BEGIN_FOO_END/ !{s/BEGIN_\([^_]*\)_END/(\1)/}' file 
BEGIN_FOO_END 
(FrOO) 
(rFOO) 
3

Esto podría funcionar para usted:

sed 'h;s/BEGIN_\(.*\)_END/(\1)/;/^(FOO)$/g' file 

Esto sólo funciona si hay una sola cuerda por línea.

Para múltiples cadenas por línea:

sed 's/BEGIN_\([^F][^_]*\|F[^O][^_]*\|FO[^O][^_]*\|FOO[^_]\+\)_END/\(\1\)/g' file 

O el más fácil de entender:

sed 's/\(BEGIN_\)FOO\(_END\)/\1\n\2/g;s/BEGIN_\([^\n_]*\)_END/(\1\)/g;s/\n/FOO/g' file