Imprimer la ligne précédente après une correspondance de model en utilisant sed?

Je veux imprimer la ligne précédente, chaque fois qu'une correspondance a été trouvée. Je connais les options grep -A et -B . Mais ma machine Solaris 5.10 ne prend pas en charge ces options.

Je veux une solution en utilisant uniquement sed .

Foo.txt :

 Name is : sara age is : 10 Name is : john age is : 20 Name is : Ron age is : 10 Name is : peggy age is : 30 

Out.txt :

 Name is : sara Name is : Ron 

Modèle que j'essaie de faire correspondre l' age is : 10 .

Mon environnement, Solaris 5.10.

 $ sed -n '/age is : 10/{x;p;d;}; x' Foo.txt Name is : sara Name is : Ron 

Ce qui précède a été testé sur GNU sed. Si sed ne prend pas en charge les commands de chaînage avec des points-virgules, essayez:

 $ sed -n -e '/age is : 10/{x;p;d;}' -ex Foo.txt Name is : sara Name is : Ron 

Comment ça marche

sed a un espace de cale et un espace de motif. Les lignes nouvelles sont lues dans l'espace de motif. L'idée de ce script est que la ligne précédente est sauvegardée dans l'espace de cale.

  • /age is : 10/{x;p;d;}

    Si la ligne actuelle contient l' age is : 10 , alors faites:

    x : permutez le motif et maintenez l'espace afin que la ligne précédente se trouve dans l'espace du motif

    p : imprimer la ligne précédente

    d : supprime l'espace de motif et commence le traitement de la ligne suivante

  • x

    Ceci n'est exécuté que sur les lignes qui ne contiennent pas d' age is : 10 . Dans ce cas, sauvegardez la ligne actuelle dans l'espace de cale.

Faire le contraire

Supposons que nous voulions imprimer les noms des personnes dont l'âge n'est pas 10:

 $ sed -n -e '/age is : 10/{x;d}' -e '/age is :/{x;p;d;}' -ex Foo.txt Name is : john Name is : peggy 

Ce qui précède ajoute une command au début, /age is : 10/{x;d} , pour ignorer tout âge-10 personnes. La command qui suit, /age is :/{x;p;d;} , accepte maintenant tous les âges restants.

POSIXly:

 $ sed -n '/age is : 10/{g 1!p } h ' file 

Pour imprimer la ligne précédente si la ligne actuelle ne correspond pas à l' age is : 10 :

 $ sed -n ' $!N /age is : 10/d P ' file 

L'ancien tampon est bon pour stocker une ligne (ou un groupe de lignes) jusqu'à ce qu'un test ultérieur s'avère vrai. En d'autres termes, il est bon de manipuler des séquences de données que vous voulez séquentielles mais qui ne sont pas encore séquentielles – car elles vous permettent de les coller set. Mais il faut aussi beaucoup de copys entre les deux tampons. Ce n'est pas si grave si vous construisez une série de lignes avec H anciennes commands – il suffit d'append – mais chaque fois que vous changez de tampon, vous copyz l'un à l'autre et vice-versa.

Lorsque vous travaillez avec une série de lignes déjà séquentielles et que vous voulez les tailler en fonction du context, la meilleure façon de procéder est d' anticiper – par opposition à l' h ancien tampon. cuonglm fait cela pour la seconde moitié de sa réponse déjà – mais vous pouvez utiliser cette logique pour l'une ou l'autre forme.

 sed '$!N;/\nage.*: 10/P;D' <infile >outfile 

Voyez, cela appenda la ligne d'input N ext suivant un délimiteur \n ewline embedded à l'espace de motif courant sur chaque ligne qui est ! pas le $ dernier. Il vérifie alors si la ligne qui vient d'être tirée correspond à un motif et, si c'est le cas, il ne l'indique que jusqu'à la première ligne dans l'espace du motif – donc seulement la ligne précédente. Enfin, il éjecte le premier \n ewline dans l'espace du motif et recommence le cycle. Ainsi, tout au long du file, vous conservez une ligne d'parsing sans intervertir inutilement les tampons.

Si je modifie un peu la command, vous pouvez voir précisément comment cela fonctionne – en glissant sur le file avec une window à deux lignes. Je vais append une command look juste avant le D :

 sed '$!N;/\nage.*: 10/P;l;D' Name is : sara Name is : sara\nage is : 10$ age is : 10\nName is : john$ Name is : john\nage is : 20$ age is : 20\nName is : Ron$ Name is : Ron Name is : Ron\nage is : 10$ age is : 10\nName is : peggy$ Name is : peggy\nage is : 30$ age is : 30$ 

C'est sa sortie. Les lignes qui se terminent par $ sont le résultat de la command look – qui rend une version échappée de l'espace des motifs à stdout. Les lignes qui ne se terminent pas par $ sont celles qui autrement seraient imprimées. Comme vous pouvez le voir, la ligne précédente est uniquement imprimée lorsque la deuxième ligne de l'espace de motif – la ligne N ext qui vient d'être introduite et qui suit le \n ewline dans l'espace du motif – correspond à votre model.

En plus des solutions déjà proposées, une autre façon d'imprimer n'est que de nommer les lignes précédant une ligne d' âge qui ne se termine pas par 10:

 sed -n '/^Name/N;/ 10$/!s/\nage.*//p' 

… qui ajoute seulement un \n ewline suivi de la ligne d'input N ext si l'espace de motif commence par la string Name et affiche seulement une ligne à sortir si l'espace de motif ne se termine pas par la string 10 et si sed peut réussir s/// ubstitute un \n ewline suivi de l' âge de la string et de tout ce qui suit jusqu'à la fin de l'espace du motif. Parce qu'il ne peut y avoir de \n ewline dans l'espace des motifs sauf en tant que résultat d'une command d'édition – telle que N ext – garantit que les seules lignes Name imprimées sont celles précédant immédiatement une ligne d' âge qui ne se termine pas dans la string 10 .

Toute la syntaxe utilisée dans la réponse ci-dessus est standard POSIX – il devrait fonctionner comme écrit avec n'importe quel sed qui supporte la norme.