118 votes

Faire en sorte que sed ignore les lignes non correspondantes

Comment puis-je faire sed filtrer les lignes correspondantes en fonction d'une expression, mais ignorer les lignes non correspondantes, au lieu de les laisser s'imprimer ?

En guise d'exemple concret, je souhaite exécuter scalac (le compilateur Scala) sur un ensemble de fichiers, et lit dans son fichier -verbose sortir le .class fichiers créés. scalac -verbose produit un certain nombre de messages, mais nous ne nous intéressons qu'à ceux de la forme [wrote some-class-name.class] . Voici ce que je fais actuellement ( |& est la façon dont bash 4.0 envoie stderr au programme suivant) :

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

Cela permettra d'extraire les noms de fichiers des messages qui nous intéressent, mais laissera passer tous les autres messages sans les modifier ! Bien sûr, nous pourrions faire ceci à la place :

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

qui fonctionne, mais qui ressemble beaucoup à un contournement du vrai problème, qui est de savoir comment instruire le sed pour ignorer les lignes non correspondantes de l'entrée. Comment faire ?

2 votes

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

285voto

mouviciel Points 36624

Si vous ne voulez pas imprimer les lignes qui ne correspondent pas, vous pouvez utiliser la combinaison de

  • -n qui indique à sed de ne pas imprimer
  • p qui indique à sed d'imprimer ce qui est trouvé

Cela donne :

sed -n 's/.../.../p'

5 votes

L'inconvénient de cette approche est que si plusieurs expressions 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' produira à la fois bar y oof sur des lignes séparées. La variante goto-label ne peut pas non plus gérer plusieurs motifs puisqu'elle supprime la ligne si le premier motif ne correspond pas.

0 votes

@Rapsey c'est parce que vous lui demandez d'imprimer deux fois. Dans la seule commande sed que vous lui avez demandé d'imprimer deux fois, chaque instance est imprimée in-situ (ou peut-être en mémoire tampon). Vous devriez soit utiliser pipe au lieu de -e, soit ne mettre que 'p' sur le dernier -e.

0 votes

@microbial, cela ne fonctionnera pas puisque le dernier drapeau p fonctionnera. sur chaque ligne correspondante par la dernière expression de substitution.

106voto

liori Points 13629

Une autre méthode consiste à utiliser de l'eau ordinaire :

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

s/// est un substitut, t sans étiquette permet d'ignorer sous condition toutes les commandes suivantes, d supprime la ligne.

Pas besoin de perl ou de grep.

(édité suite à la suggestion de Nicholas Riley)

3 votes

Sous OS X 10.8.2, j'ai dû séparer tx y d avec une nouvelle ligne plutôt qu'un point-virgule, car j'obtenais undefined label 'x;d;:x' .

6 votes

C'est encore mieux : sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x' (suggéré dans un commentaire sur un question similaire ).

1 votes

't' sera transféré à la fin du script si aucune étiquette n'est fournie, ce qui est plus simple : sed -e 's/.../.../' -e 't' -e 'd' .

3voto

Amessihel Points 1483

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

  • Premièrement, citant la réponse d'un SE Unix 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 des commandes entre accolades {} (séparés 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 Ce document pour plus de détails) :

  • Code :

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

    keke

2voto

Greg Hewgill Points 356191

Utiliser Perl :

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'

1voto

Amber Points 159296

Pourquoi l'utilisation de grep Les lignes de filtrage sont-elles un moyen de "contourner le problème" ? C'est exactement ce que grep et les canalisations ont été utilisées pour la fabrication de conçu à enchaîner. Votre deuxième option est très bien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X