GNU/Linux >> Tutoriales Linux >  >> Linux

¿Extraer una expresión regular combinada con 'sed' sin imprimir los caracteres circundantes?

A todos los médicos 'sed':

¿Cómo puede obtener 'sed' para extraer una expresión regular que coincida en una línea
?

En otras palabras, solo quiero la cadena correspondiente a la expresión regular
con todos los caracteres que no coinciden de la línea contenedora eliminados.

Intenté usar la función de referencia inversa como se muestra a continuación

regular expression to be isolated 
         gets `inserted` 
              here     
               |
               v  
 sed -n 's/.*( ).*/1/p 

esto funciona para algunas expresiones como

 sed -n 's/.*(CONFIG_[a-zA-Z0-9_]*).*/1/p 

que extrae ordenadamente todos los nombres de macro que comienzan con 'CONFIG_ ....' (que se encuentra en algún archivo '*.h') y los imprime línea por línea

          CONFIG_AT91_GPIO
          CONFIG_DRIVER_AT91EMAC
                   .
                   .   
          CONFIG_USB_ATMEL
          CONFIG_USB_OHCI_NEW
                   .
                 e.t.c. 

PERO lo anterior se descompone en algo como

  sed -n 's/.*([0-9][0-9]*).*/1/p 

esto siempre devuelve un solo dígito como

                 7
                 9
                 .
                 .  
                 6

en lugar de extraer un campo numérico contiguo como.

              8908078
              89670890  
                 .
                 .  
                 .
               23019   
                 .
               e.t.c.  

P.D.:agradecería recibir comentarios sobre cómo se logra esto en 'sed'.
Sé cómo hacer esto con 'grep' y 'awk'
Me gustaría saber si mi – aunque limitado:la comprensión de
'sed' tiene lagunas y si hay una manera de hacer esto en 'sed' que
simplemente he pasado por alto.

Respuesta aceptada:

Cuando una expresión regular contiene grupos, puede haber más de una forma de hacer coincidir una cadena con ella:las expresiones regulares con grupos son ambiguas. Por ejemplo, considere la expresión regular ^.*([0-9][0-9]*)$ y la cadena a12 . Hay dos posibilidades:

  • Hacer coincidir a contra .* y 2 contra [0-9]*; 1 coincide con [0-9] .
  • Coincide con a1 contra .* y la cadena vacía contra [0-9]*; 2 coincide con [0-9] .

Sed, como todas las demás herramientas de expresión regular, aplica la regla de coincidencia más antigua:primero intenta hacer coincidir la primera parte de longitud variable con una cadena que sea lo más larga posible. Si encuentra una manera de hacer coincidir el resto de la cadena con el resto de la expresión regular, bien. De lo contrario, sed intenta la siguiente coincidencia más larga para la primera parte de longitud variable y vuelve a intentarlo.

Aquí, la coincidencia con la cadena más larga primero es a1 contra .* , por lo que el grupo solo coincide con 2 . Si desea que el grupo comience antes, algunos motores de expresiones regulares le permiten hacer el .* menos codicioso, pero sed no tiene esa característica. Entonces necesitas eliminar la ambigüedad con algún ancla adicional. Especifique que el .* inicial no puede terminar con un dígito, por lo que el primer dígito del grupo es la primera coincidencia posible.

  • Si el grupo de dígitos no puede estar al principio de la línea:

    sed -n 's/^.*[^0-9]([0-9][0-9]*).*/1/p'
    
  • Si el grupo de dígitos puede estar al principio de la línea, y su sed admite el ? operador para piezas opcionales:

    sed -n 's/^(.*[^0-9])?([0-9][0-9]*).*/1/p'
    
  • Si el grupo de dígitos puede estar al principio de la línea, siguiendo las construcciones estándar de expresiones regulares:

    sed -n -e 's/^.*[^0-9]([0-9][0-9]*).*/1/p' -e t -e 's/^([0-9][0-9]*).*/1/p'
    

Por cierto, es la misma regla de coincidencia más antigua que hace que [0-9]* hacer coincidir los dígitos después del primero, en lugar del subsiguiente .* .

Tenga en cuenta que si hay múltiples secuencias de dígitos en una línea, su programa siempre extraerá la última secuencia de dígitos, nuevamente debido a la regla de coincidencia más antigua aplicada al .* inicial. . Si desea extraer la primera secuencia de dígitos, debe especificar que lo que viene antes es una secuencia de no dígitos.

sed -n 's/^[^0-9]*([0-9][0-9]*).*$/1/p'

En términos más generales, para extraer la primera coincidencia de una expresión regular, debe calcular la negación de esa expresión regular. Si bien esto siempre es teóricamente posible, el tamaño de la negación crece exponencialmente con el tamaño de la expresión regular que estás negando, por lo que a menudo no es práctico.

Relacionado:¿No puede habilitar la compatibilidad con SMART para el disco duro externo?

Considere su otro ejemplo:

sed -n 's/.*(CONFIG_[a-zA-Z0-9_]*).*/1/p'

Este ejemplo en realidad presenta el mismo problema, pero no lo ve en las entradas típicas. Si lo alimentas hello CONFIG_FOO_CONFIG_BAR , luego el comando anterior imprime CONFIG_BAR , no CONFIG_FOO_CONFIG_BAR .

Hay una forma de imprimir la primera coincidencia con sed, pero es un poco complicada:

sed -n -e 's/(CONFIG_[a-zA-Z0-9_]*).*/n1/' -e T -e 's/^.*n//' -e p

(Suponiendo que su sed admita n para significar una nueva línea en los s texto de reemplazo). Esto funciona porque sed busca la primera coincidencia de la expresión regular, y no intentamos hacer coincidir lo que precede a CONFIG_… poco. Como no hay una nueva línea dentro de la línea, podemos usarla como marcador temporal. El T el comando dice que te rindas si los s anteriores el comando no coincide.

Cuando no sepa cómo hacer algo en sed, cambie a awk. El siguiente comando imprime la primera coincidencia más larga de una expresión regular:

awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'

Y si desea simplificar las cosas, use Perl.

perl -l -ne '/[0-9]+/ && print $&'       # first match
perl -l -ne '/^.*([0-9]+)/ && print $1'  # last match

Linux
  1. Reemplace las comillas tipográficas con el comando sed de Linux

  2. Adición Con 'sed'?

  3. ¿Solo devolver la cadena coincidente en Sed?

  4. ¿Necesita escapar de los caracteres Regex en Sed para que se interpreten como caracteres Regex?

  5. ¿Usando Sed con caracteres especiales?

Usando el comando tr en Linux para jugar con personajes

Encuentre archivos con caracteres de Windows ilegales en el nombre en Linux

¿Cómo puedo usar grep para hacer coincidir pero sin imprimir las coincidencias?

La agrupación de expresiones regulares coincide con la biblioteca de expresiones regulares de C++ 11

Usar find y tar con archivos con caracteres especiales en el nombre

¿Cómo reemplazar recursivamente los caracteres con sed?