Demander à sed d'ignorer les lignes non correspondantes

91

Comment puis-je faire sed des lignes de filtrage correspondant à une expression, mais ignorer les lignes non correspondantes, au lieu de les laisser s'imprimer?

À titre d'exemple réel, je veux exécuter scalac(le compilateur Scala) sur un ensemble de fichiers et lire à partir de sa -verbosesortie les .classfichiers créés. scalac -verboserenvoie un tas de messages, mais nous ne sommes intéressés que par ceux du formulaire [wrote some-class-name.class]. Ce que je fais actuellement est la suivante ( |&est la manière de bash 4.0 de diriger stderr vers le programme suivant):

$ scalac -verbose some-file.scala ... |& sed 's/^\[wrote \(.*\.class\)\]$/\1/'

Cela extraira les noms de fichiers des messages qui nous intéressent, mais laissera également passer tous les autres messages inchangés! Bien sûr, nous pourrions faire à la place ceci:

$ scalac -verbose some-file.scala ... |& grep '^\[wrote .*\.class\]$' |
  sed 's/^\[wrote \(.*\.class\)\]$/\1/'

qui fonctionne mais ressemble beaucoup à contourner le vrai problème, qui est de savoir comment demander sedà ignorer les lignes non correspondantes de l'entrée. Alors, comment faisons-nous cela?

Paggas
la source
2
La réponse acceptée devrait être celle de mouviciel: stackoverflow.com/a/1665574/869951
Oliver

Réponses:

89

Une autre façon avec le sed plain:

sed -e 's/.../.../;t;d'

s///est une substitution, tsans aucune étiquette saute conditionnellement toutes les commandes suivantes,d supprime la ligne.

Pas besoin de perl ou de grep.

(édité après la suggestion de Nicholas Riley)

liori
la source
3
Sous OS X 10.8.2, je devais séparer txet davec une nouvelle ligne plutôt qu'un point-virgule comme j'obtenais undefined label 'x;d;:x'.
davidchambers
6
Mieux encore: sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x'(suggéré dans un commentaire sur une question similaire ).
davidchambers
1
« t » transférera à la fin du script si aucune étiquette est fourni, de manière plus simple: sed -e 's/.../.../' -e 't' -e 'd'.
Nicholas Riley
Les gens ne connaissent pas la signification de l' -eoption, alors ne le mentionnez pas en général.
Fredrick Gauss
242

Si vous ne souhaitez pas imprimer des lignes qui ne correspondent pas, vous pouvez utiliser la combinaison de

  • -n option qui dit à sed de ne pas imprimer
  • p indicateur qui indique à sed d'afficher ce qui correspond

Cela donne:

sed -n 's/.../.../p'
mouviciel
la source
3
Un inconvénient de cette approche est que si vous avez plusieurs expressions qui correspondent, le résultat sera également imprimé plusieurs fois. Par exemple: echo foo | sed -n -e 's/foo/bar/p' -e 's/bar/oof/p'affichera les deux baret oofsur des lignes séparées. Bien que la variété goto-label ne puisse pas non plus gérer plusieurs modèles puisqu'elle supprimera la ligne si le premier modèle ne correspond pas.
Rapsey
@Rapsey c'est parce que vous lui dites d'imprimer deux fois. Dans la commande sed unique, vous lui avez dit d'imprimer deux fois, chaque instance est imprimée in situ (ou peut-être tamponnée). Vous auriez soit à pipe au lieu de -e, soit à ne mettre «p» que sur le dernier -e.
microbien
@Rapsey: voir ma réponse sur ce point pertinent
Amessihel
@microbial, cela ne fonctionnera pas car le dernier indicateur p fonctionnera sur chaque ligne correspondante par la dernière expression de substitution.
Amessihel le
1

Utilisez Perl:

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'
Greg Hewgill
la source
0
sed -n '/.../!p'

Il n'y a pas besoin de substitution.

Évidemment
la source
0

Rapsey a soulevé un point pertinent concernant les expressions de substitutions multiples.

  • Tout d'abord, citer une réponse Unix SE , vous pouvez "préfixer la plupart des commandes sed avec une adresse pour limiter les lignes auxquelles elles s'appliquent".
  • Deuxièmement, vous pouvez regrouper les commandes entre accolades {}(séparées par un point-virgule ;ou une nouvelle ligne)
  • Troisièmement, ajoutez le drapeau d'impression p sur la dernière substitution

Syntaxe:

sed -n -e '/^given_regexp/ {s/regexp1/replacement1/flags1;[...];s/regexp1/replacement1/flagsnp}'

Exemple (voir le document Here pour plus de détails):

  • Code:

    sed -n -e '/^ha/ {s/h/k/gp;s/a/e/g}' <<SAMPLE
    haha
    hihi
    SAMPLE
    
  • Résultat:

    kaka
    
Amessihel
la source