Quiero usar sed
para reemplazar cualquier cosa en una cadena entre la primera AB
y la primera aparición de AC
(inclusive) con XXX
.
Por ejemplo , tengo esta cadena (esta cadena es solo para una prueba):
ssABteAstACABnnACss
y me gustaría una salida similar a la siguiente: ssXXXABnnACss
.
Hice esto con perl
:
$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss
pero quiero implementarlo con sed
. Lo siguiente (usando la expresión regular compatible con Perl) no funciona:
$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss
text-processing
sed
regular-expression
بارپابابا
fuente
fuente
Respuestas:
Las expresiones regulares de Sed coinciden con el partido más largo. Sed no tiene equivalente a no codicioso.
Obviamente lo que queremos hacer es unir
AB
,seguido de
AC
,seguido de
AC
Desafortunadamente,
sed
no puedo hacer el n. ° 2, al menos no para una expresión regular de varios caracteres. Por supuesto, para una expresión regular de un solo carácter como@
(o incluso[123]
), podemos hacer[^@]*
o[^123]*
. Por esto se puede evitar limitaciones de sed, cambiando todas las apariciones deAC
a@
y luego la búsqueda deAB
,seguido de
@
,seguido de
@
Me gusta esto:
La última parte cambia instancias inigualables de
@
regreso aAC
.Pero, por supuesto, este es un enfoque imprudente, ya que la entrada ya podría contener
@
caracteres, por lo que, al unirlos, podríamos obtener falsos positivos. Sin embargo, dado que ninguna variable de shell tendrá nunca un carácter NUL (\x00
), es probable que NUL sea un buen carácter para usar en la solución anterior en lugar de@
:El uso de NUL requiere GNU sed. (Para asegurarse de que las características de GNU estén habilitadas, el usuario no debe haber configurado la variable de shell POSIXLY_CORRECT).
Si está utilizando sed con el
-z
indicador de GNU para manejar la entrada separada por NUL, como la salida defind ... -print0
, entonces NUL no estará en el espacio del patrón y NUL es una buena opción para la sustitución aquí.Aunque NUL no puede estar en una variable bash, es posible incluirlo en un
printf
comando. Si su cadena de entrada puede contener cualquier carácter, incluido NUL, consulte la respuesta de Stéphane Chazelas que agrega un método de escape inteligente.fuente
echo
oprintf
un `\ 000 'bien en bash (o la entrada podría provenir de un archivo). Pero, en general, una cadena de texto probablemente no tenga NUL.AC
deAC@
nuevo?Algunas
sed
implementaciones tienen soporte para eso.ssed
tiene un modo PCRE:AT&T ast sed tiene conjunción y negación cuando se usan expresiones regulares aumentadas :
Portablemente, puede usar esta técnica: reemplace la cadena final (aquí
AC
) con un solo carácter que no aparece en la cadena inicial o final (como:
aquí) para que pueda hacerlos/AB[^:]*://
, y en caso de que ese carácter pueda aparecer en la entrada , utilice un mecanismo de escape que no entre en conflicto con las cadenas de inicio y fin.Un ejemplo:
Con GNU
sed
, un enfoque es utilizar la nueva línea como el personaje de reemplazo. Debido a quesed
procesa una línea a la vez, la nueva línea nunca ocurre en el espacio del patrón, por lo que se puede hacer:Eso generalmente no funciona con otras
sed
implementaciones porque no son compatibles[^\n]
. Con GNUsed
debe asegurarse de que la compatibilidad POSIX no esté habilitada (como con la variable de entorno POSIXLY_CORRECT).fuente
No, las expresiones regulares sed no tienen coincidencias no codiciosas.
Puede hacer coincidir todo el texto hasta la primera aparición
AC
utilizando "cualquier cosa que no contengaAC
" seguido deAC
, que hace lo mismo que Perl.*?AC
. La cuestión es que "cualquier cosa que no contengaAC
" no se puede expresar fácilmente como una expresión regular: siempre hay una expresión regular que reconoce la negación de una expresión regular, pero la expresión regular de la negación se complica rápidamente. Y en sed portátil, esto no es posible en absoluto, porque la negación regex requiere agrupar una alternancia que está presente en expresiones regulares extendidas (por ejemplo, en awk) pero no en expresiones regulares básicas portátiles. Algunas versiones de sed, como GNU sed, tienen extensiones de BRE que permiten expresar todas las expresiones regulares posibles.Debido a la dificultad de negar una expresión regular, esto no se generaliza bien. Lo que puede hacer en su lugar es transformar la línea temporalmente. En algunas implementaciones de sed, puede usar nuevas líneas como marcador, ya que no pueden aparecer en una línea de entrada (y si necesita varios marcadores, use nueva línea seguida de un carácter variable).
Sin embargo, tenga en cuenta que la barra diagonal inversa no funciona en un juego de caracteres con algunas versiones sed. En particular, esto no funciona en GNU sed, que es la implementación de sed en Linux no incrustado; en GNU sed puedes usar
\n
en su lugar:En este caso específico, es suficiente reemplazar el primero
AC
por una nueva línea. El enfoque que presenté anteriormente es más general.Un enfoque más poderoso en sed es guardar la línea en el espacio de espera, eliminar todo excepto la primera parte "interesante" de la línea, intercambiar el espacio de espera y el espacio de patrón o agregar el espacio de patrón al espacio de espera y repetir. Sin embargo, si comienza a hacer cosas que son tan complicadas, realmente debería pensar en cambiar a awk. Awk tampoco tiene una coincidencia no codiciosa, pero puede dividir una cadena y guardar las partes en variables.
fuente
s/\n//g
elimina todas las líneas nuevas.sed - correspondencia no codiciosa por Christoph Sieghart
fuente
En su caso, puede negar el cierre de char de esta manera:
fuente
AB
y la primera aparición deAC
conXXX
...", y dassABteAstACABnnACss
como ejemplo de entrada. Esta respuesta funciona para ese ejemplo , pero no responde la pregunta en general. Por ejemplo,ssABteCstACABnnACss
también debería producir el resultadoaaXXXABnnACss
, pero su comando pasa esta línea sin cambios.La solución es bastante simple.
.*
es codicioso, pero no es absolutamente codicioso. Considera hacer coincidirssABteAstACABnnACss
contra la expresión regularAB.*AC
. LoAC
que sigue.*
debe tener una coincidencia. El problema es que debido a que.*
es codicioso, el siguienteAC
coincidirá con el último enAC
lugar del primero..*
se come el primeroAC
mientras que el literalAC
en la expresión regular coincide con el último en ssABteAstACABnn AC ss. Para evitar que esto suceda, simplemente reemplace el primeroAC
con algo ridículo para diferenciarlo del segundo y de cualquier otra cosa.El codicioso
.*
ahora se detendrá al pie de-foobar-
adentrossABteAst-foobar-ABnnACss
porque no hay otro-foobar-
que esto-foobar-
, y la expresión regular-foobar-
DEBE tener una coincidencia. El problema anterior era que la expresión regularAC
tenía dos coincidencias, pero como.*
era codicioso,AC
se seleccionó la última coincidencia . Sin embargo, con-foobar-
solo una coincidencia es posible, y esta coincidencia demuestra que.*
no es absolutamente codicioso. La parada de autobús para.*
ocurre donde solo queda un partido para el resto de la expresión regular siguiente.*
.Tenga en cuenta que esta solución fallará si
AC
aparece una antes de la primeraAB
porqueAC
se reemplazará la incorrecta-foobar-
. Por ejemplo, después de la primerased
sustitución, seACssABteAstACABnnACss
convierte en-foobar-ssABteAstACABnnACss
; por lo tanto, no se puede encontrar una coincidencia en contraAB.*-foobar-
. Sin embargo, si la secuencia es siempre ... AB ... AC ... AB ... AC ..., entonces esta solución tendrá éxito.fuente
Una alternativa es cambiar la cadena para que desee la coincidencia codiciosa
Use
rev
para revertir la cadena, revierta sus criterios de coincidencia, usesed
de la manera habitual y luego revierta el resultado ...fuente