Comment sortir uniquement les groupes capturés avec sed?

Est-il possible de dire à sed de n’émettre que des groupes capturés? Par exemple, compte tenu de l’entrée:

 This is a sample 123 text and some 987 numbers 

et motif:

 /([\d]+)/ 

Est-ce que je pourrais obtenir seulement 123 et 987 sorties de la manière formatée par les références arrières?

La clé pour que cela fonctionne est de dire à sed d’exclure ce que vous ne voulez pas afficher et de spécifier ce que vous voulez.

 ssortingng='This is a sample 123 text and some 987 numbers' echo "$ssortingng" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p' 

Cela dit:

  • n’imprimez pas chaque ligne par défaut ( -n )
  • exclure zéro ou plus de non-chiffres
  • inclure un ou plusieurs chiffres
  • exclure un ou plusieurs non-chiffres
  • inclure un ou plusieurs chiffres
  • exclure zéro ou plus de non-chiffres
  • imprimer la substitution ( p )

En général, vous capturez des groupes en utilisant des parenthèses et affichez ce que vous capturez en utilisant une référence arrière:

 echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/' 

va sortir “bar”. Si vous utilisez -r ( -E pour OS X) pour une expression régulière étendue, vous n’avez pas besoin d’échapper aux parenthèses:

 echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/' 

Il peut y avoir jusqu’à 9 groupes de capture et leurs références arrière. Les références arrière sont numérotées dans l’ordre d’apparition des groupes, mais elles peuvent être utilisées dans n’importe quel ordre et peuvent être répétées:

 echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/' 

sorties “une barre a”.

Si vous avez GNU grep (cela peut aussi fonctionner dans BSD, y compris OS X):

 echo "$ssortingng" | grep -Po '\d+' 

ou des variantes telles que:

 echo "$ssortingng" | grep -Po '(?< =\D )(\d+)' 

L'option -P active les expressions régulières compatibles avec Perl. Voir man 3 pcrepattern ou man 3 pcresyntax .

Sed a jusqu’à neuf modèles mémorisés, mais vous devez utiliser des parenthèses échappées pour mémoriser des parties de l’expression régulière.

Voir ici pour des exemples et plus de détails

vous pouvez utiliser grep

 grep -Eow "[0-9]+" file 

Je crois que le modèle donné dans la question n’a été donné qu’à titre d’exemple et que l’objective était de faire correspondre tout modèle.

Si vous avez un sed avec l’extension GNU permettant l’insertion d’une nouvelle ligne dans l’espace du pattern, une suggestion est:

 > set ssortingng = "This is a sample 123 text and some 987 numbers" > > set pattern = "[0-9][0-9]*" > echo $ssortingng | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p" 123 987 > set pattern = "[az][az]*" > echo $ssortingng | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p" his is a sample text and some numbers 

Ces exemples sont avec tcsh (oui, je sais que c’est le mauvais shell) avec CYGWIN. (Edit: Pour bash, remove set et les espaces autour de =.)

Essayer

 sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p" 

Je l’ai eu sous cygwin:

 $ (echo "asdf"; \ echo "1234"; \ echo "asdf1234adsf1234asdf"; \ echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \ sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p" 1234 1234 1234 1 2 3 4 5 6 7 8 9 $ 

Abandonnez et utilisez Perl

Comme sed ne le coupe pas, jetons simplement la serviette et utilisons Perl, au moins c’est LSB alors que les extensions GNU de grep ne le sont pas 🙂

  • Imprimez l’intégralité de la partie correspondante, aucun groupe correspondant ou recherche en retard:

     cat <  

    Sortie:

     12 3456 
  • Correspondance unique par ligne, champs de données souvent structurés:

     cat <  

    Sortie:

     1 34 

    Avec lookbehind:

     cat <  
  • Plusieurs champs:

     cat <  

    Sortie:

     1 2 34 56 
  • Plusieurs correspondances par ligne, souvent des données non structurées:

     cat <  

    Sortie:

     1 34 78 

    Avec lookbehind:

     cat EOS< < | perl -lane 'print m/(?<=a)(\d+)/g' a1 b2 a34 b56 a78 b90 EOS 

    Sortie:

     1 3478 

course de chiffres

Cette réponse fonctionne avec n’importe quel nombre de groupes de chiffres. Exemple:

 $ echo 'Num123that456are7899900contained0018166intext' | > sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 123 456 7899900 0018166 

Réponse élargie.

Est-il possible de dire à sed de n’émettre que des groupes capturés?

Oui. remplacer tout le texte par le groupe de capture:

 $ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/' 123 s/[^0-9]* # several non-digits \([0-9]\{1,\}\) # followed by one or more digits [^0-9]* # and followed by more non-digits. /\1/ # gets replaced only by the digits. 

Ou avec une syntaxe étendue (moins de backquotes et permettre l’utilisation de +):

 $ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/' 123 

Pour éviter d’imprimer le texte original lorsqu’il n’y a pas de numéro, utilisez:

 $ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p' 
  • (-n) N’imprimez pas l’entrée par défaut.
  • (/ p) imprimer uniquement si un remplacement a été effectué.

Et pour faire correspondre plusieurs numéros (et aussi les imprimer):

 $ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp' 123 456 

Cela fonctionne pour n’importe quel nombre de chiffres:

 $ str='Test Num(s) 123 456 7899900 contained as0018166df in text' $ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 123 456 7899900 0018166 

Qui est très similaire à la commande grep:

 $ str='Test Num(s) 123 456 7899900 contained as0018166df in text' $ echo "$str" | grep -Po '\d+' 123 456 7899900 0018166 

A propos de \ d

et pattern: /([\d]+)/

Sed ne reconnaît pas la syntaxe ‘\ d’ (raccourci). L’équivalent ascii utilisé ci-dessus [0-9] n’est pas exactement équivalent. La seule solution alternative consiste à utiliser une classe de caractères: ‘[[: digit:]] `.

La réponse sélectionnée utilise ces “classes de caractères” pour créer une solution:

 $ str='This is a sample 123 text and some 987 numbers' $ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p' 

Cette solution ne fonctionne que pour (exactement) deux séries de chiffres.

Bien sûr, comme la réponse est exécutée dans le shell, nous pouvons définir quelques variables pour raccourcir cette réponse:

 $ str='This is a sample 123 text and some 987 numbers' $ d=[[:digit:]] D=[^[:digit:]] $ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p" 

Mais, comme cela a déjà été expliqué, utiliser une commande s/…/…/gp est préférable:

 $ str='This is 75577 a sam33ple 123 text and some 987 numbers' $ d=[[:digit:]] D=[^[:digit:]] $ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp" 75577 33 123 987 

Cela couvrira à la fois les exécutions répétées de chiffres et l’écriture d’une commande courte.

Ce n’est pas ce que l’OP a demandé (capturer des groupes) mais vous pouvez extraire les nombres en utilisant:

 S='This is a sample 123 text and some 987 numbers' echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d' 

Donne ce qui suit:

 123 987