sed: elimina texto entre una cadena hasta que aparezca por primera vez otra cadena

sed: elimina texto entre una cadena hasta que aparezca por primera vez otra cadena

Imagina que tengo algo como el siguiente texto:

El veloz zorro marrón salta en 2012 y 2013.

Y me gustaría eliminar la parte de "zorro", incluidos los cuatro números, pero solo en la primera aparición, por lo que termino con:

El marrón rápido y 2013

Algo como esto...:

echo "El veloz zorro marrón salta en 2012 y 2013" \
   | sed "s/fox.*\([0-9]\{4\}\)//g"

...me trae:

El marrón rápido

Entonces eliminó todo, incluida la última aparición de los cuatro números.

¿Algunas ideas?

Respuesta1

Las expresiones regulares POSIX utilizadas por sed(tanto la versión "básica" como la "extendida") no admiten coincidencias no codiciosas. (Aunque existen algunas soluciones, como usar [^0-9]*en lugar de .*, dejan de ser confiables si las entradas varían mucho).

Lo que necesita se puede lograr en Perl utilizando el ?cuantificador no codicioso:

echo "The quick brown fox jumps in 2012 and 2013" \
   | perl -pe 's/fox.*?([0-9]{4})//g'

Es posible que también desees eliminar un espacio adicional.

Respuesta2

Suponiendo que quieras usarsolosed y quieres que el final del partido sea el primer grupo de dígitos, sin importar cuál sea la palabra después de los dígitos, esto funciona:

echo "El veloz zorro marrón salta en 2012 y 2013" \
   | sed "s/fox[^0-9][^0-9]*[0-9][0-9]* //"

El patrón funciona haciendo coincidir fox, seguido de uno o más números que no son dígitos [^0-9][^0-9]*, seguido de 1 o más dígitos [0-9][0-9]*. Este patrón funcionará con un número arbitrario de dígitos, no solo 4. Si desea hacer coincidir exactamente 4 dígitos, cámbielo a:

echo "El veloz zorro marrón salta en 2012 y 2013" \
   | sed "s/fox[^0-9]*\([0-9]\{4\}\) //"

Respuesta3

No especificasteexactamentecuáles son sus requisitos. Es posible que desee un proceso de varios pasos. Elija una cadena que sepa que no aparecerá en su entrada (por ejemplo, ####):

echo "El veloz zorro marrón salta sobre 42 perros perezosos en 2012 y 2013". \
  | sed \
        -e "s/[0-9]\{4\}/&####/" \
        -e "s/zorro.*####//" \
        -e "s/####//"

(Comando excesivamente doblado para facilitar la lectura). Las -e "s/[0-9]\{4\}/&####/"inyecciones ####despuésla primeranúmero de cuatro dígitos. (Advertencia: esto cambiará 65536a 6553####6.)
-e "s/fox.*####//"afecta a las líneas que contienen foxy ####, es decir, líneas que contienen al menos un número de cuatro dígitos, y luego se elimina foxdesdela primeranúmero de cuatro dígitos.
-e "s/####//", por supuesto, limpia cualquier ####cadena que quede de líneas que contienen un número de cuatro dígitos pero no fox.

Para eliminar también un espacio después del número, si lo hay,

echo "El veloz zorro marrón salta sobre 42 perros perezosos en 2012 y 2013". \
  | sed \
        -e "s/[0-9]\{4\}/&####/" \
        -e "s/zorro.*#### //" \
        -e "s/zorro.*####//" \
        -e "s/####//"

Advertencia: puede agregar gtodos los scomandos, pero, dado que todavía usa .*, que es la raíz de su problema, aún no se manejará.

One fox jumps in 2012 and 2013, another fox will jump in 2014 and 2015.

de la forma que probablemente quieras. Y por supuesto tunoquiero agregar gporque "s/[0-9]\{4\}/&####/"luego se inyectará ####despuéscadanúmero de cuatro dígitos, anulando todo el punto. Entonces "s/fox.*####//"terminará actuando igual "s/fox.*[0-9]\{4\}//"(su comando original sin los caracteres que no contribuyen); es decir, cambiará

El veloz zorro marrón salta en 2012 y 2013.

a

El rápido zorro marrón salta en 2012#### y 2013####.

y luego a

El marrón rápido.

información relacionada