GNU/Linux >> Tutoriels Linux >  >> Linux

Extraire une expression régulière correspondant à "sed" sans imprimer les caractères environnants ?

À tous les médecins "sed" :

Comment pouvez-vous obtenir "sed" pour extraire une expression régulière à laquelle il correspond dans une
ligne ?

En d'autres termes, je veux juste la chaîne correspondant à l'expression régulière
avec tous les caractères non correspondants de la ligne contenante supprimés.

J'ai essayé d'utiliser la fonction de référence arrière comme ci-dessous

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

cela fonctionne pour certaines expressions comme

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

qui extrait soigneusement tous les noms de macros commençant par 'CONFIG_….' (trouvé dans certains fichiers '*.h') et les imprime ligne par ligne

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

MAIS ce qui précède tombe en panne pour quelque chose comme

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

cela renvoie toujours des chiffres uniques comme

                 7
                 9
                 .
                 .  
                 6

plutôt que d'extraire un champ numérique contigu tel que.

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

P.S. :Je serais reconnaissant de recevoir des commentaires sur la manière dont cela est réalisé dans 'sed'.
Je sais comment faire cela avec 'grep' et 'awk'
J'aimerais savoir si mon - quoique limité - la compréhension de
'sed' a des lacunes et s'il y a un moyen de le faire dans 'sed' que j'ai simplement
négligé.

Réponse acceptée :

Lorsqu'une expression rationnelle contient des groupes, il peut y avoir plus d'une façon de faire correspondre une chaîne à celle-ci :les expressions rationnelles avec des groupes sont ambiguës. Par exemple, considérez l'expression régulière ^.*([0-9][0-9]*)$ et la chaîne a12 . Il y a deux possibilités :

  • Reconnaître a contre .* et 2 contre [0-9]*; 1 correspond à [0-9] .
  • Reconnaître a1 contre .* et la chaîne vide contre [0-9]*; 2 correspond à [0-9] .

Sed, comme tous les autres outils d'expression régulière, applique la première règle de correspondance la plus longue :il essaie d'abord de faire correspondre la première partie de longueur variable avec une chaîne aussi longue que possible. S'il trouve un moyen de faire correspondre le reste de la chaîne avec le reste de l'expression régulière, très bien. Sinon, sed essaie la prochaine correspondance la plus longue pour la première portion de longueur variable et essaie à nouveau.

Ici, la correspondance avec la chaîne la plus longue en premier est a1 contre .* , donc le groupe ne correspond qu'à 2 . Si vous souhaitez que le groupe démarre plus tôt, certains moteurs d'expressions régulières vous permettent de créer le .* moins gourmand, mais sed n'a pas une telle fonctionnalité. Vous devez donc supprimer l'ambiguïté avec une ancre supplémentaire. Spécifiez que le premier .* ne peut pas se terminer par un chiffre, de sorte que le premier chiffre du groupe est la première correspondance possible.

  • Si le groupe de chiffres ne peut pas être en début de ligne :

    sed -n 's/^.*[^0-9]([0-9][0-9]*).*/1/p'
    
  • Si le groupe de chiffres peut être au début de la ligne et que votre sed prend en charge le ? opérateur pour les pièces optionnelles :

    sed -n 's/^(.*[^0-9])?([0-9][0-9]*).*/1/p'
    
  • Si le groupe de chiffres peut être au début de la ligne, respectez les constructions d'expression régulière standard :

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

Soit dit en passant, c'est cette même règle de correspondance la plus longue qui fait [0-9]* faire correspondre les chiffres après le premier, plutôt que le suivant .* .

Notez que s'il y a plusieurs séquences de chiffres sur une ligne, votre programme extraira toujours la dernière séquence de chiffres, encore une fois en raison de la première règle de correspondance la plus longue appliquée au .* initial . Si vous souhaitez extraire la première séquence de chiffres, vous devez spécifier que ce qui précède est une séquence de non-chiffres.

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

Plus généralement, pour extraire la première correspondance d'une expression rationnelle, vous devez calculer la négation de cette expression rationnelle. Bien que cela soit théoriquement toujours possible, la taille de la négation augmente de manière exponentielle avec la taille de l'expression rationnelle que vous niez, ce qui est souvent peu pratique.

En relation :Impossible d'activer la prise en charge SMART pour le disque dur externe ?

Considérez votre autre exemple :

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

Cet exemple présente en fait le même problème, mais vous ne le voyez pas sur les entrées typiques. Si vous le nourrissez hello CONFIG_FOO_CONFIG_BAR , la commande ci-dessus imprime CONFIG_BAR , pas CONFIG_FOO_CONFIG_BAR .

Il existe un moyen d'imprimer la première correspondance avec sed, mais c'est un peu délicat :

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

(En supposant que votre sed supporte n pour signifier une nouvelle ligne dans les s texte de remplacement.) Cela fonctionne car sed recherche la correspondance la plus ancienne de l'expression régulière, et nous n'essayons pas de faire correspondre ce qui précède le CONFIG_… bit. Puisqu'il n'y a pas de nouvelle ligne à l'intérieur de la ligne, nous pouvons l'utiliser comme marqueur temporaire. Le T la commande dit d'abandonner si les s précédents la commande ne correspondait pas.

Lorsque vous ne savez pas comment faire quelque chose dans sed, tournez-vous vers awk. La commande suivante imprime la première correspondance la plus longue d'une expression régulière :

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

Et si vous avez envie de faire simple, utilisez Perl.

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

Linux
  1. Supprimez les cinq premiers caractères de n'importe quelle ligne d'un fichier texte sous Linux avec sed

  2. Quel est le problème avec mon regex lookahead dans GNU sed?

  3. Trouver des fichiers avec des caractères Windows illégaux dans le nom sous Linux

  4. Comment puis-je utiliser grep pour correspondre mais sans imprimer les correspondances ?

  5. Le regroupement Regex correspond à la bibliothèque regex C++ 11

Remplacez les guillemets intelligents par la commande Linux sed

Manipulation de texte en ligne de commande avec sed

Ne renvoyer que la chaîne correspondante dans Sed ?

Besoin d'échapper aux caractères Regex dans Sed pour être interprétés comme des caractères Regex ?

Utilisation de la commande tr sous Linux pour jouer avec des personnages

Comment remplacer récursivement les caractères par sed ?