GNU/Linux >> Tutoriels Linux >  >> Linux

Imprimer la ligne précédente si la condition est remplie

Autre option :inverser le fichier et imprimer le suivant ligne si la condition correspond :

tac file | awk '$1 == "BB" && $2 > 1 {getline; print}' | tac

Concernant la généralité

Je pense qu'il faut mentionner que la solution la plus générale à cette classe de problème implique deux passes :

  • la première passe pour ajouter un numéro de ligne décimal ($REC) au début de chaque ligne, regroupant efficacement les lignes en enregistrements par $REC
  • la deuxième passe à déclencher sur la première instance de chaque nouvelle valeur de $REC en tant que limite d'enregistrement (réinitialisant $CURREC), puis en continuant dans l'idiome AWK natif concernant les enregistrements à suivre correspondant à $CURREC.

Dans le fichier intermédiaire, une séquence de chiffres décimaux suivie d'un séparateur (pour des raisons humaines, généralement une tabulation ou un espace ajouté) est analysée (c'est-à-dire coupée conceptuellement) comme hors bande par rapport au fichier de base.

Monstre de collage de ligne de commande

Même confiné à la ligne de commande, il est facile de s'assurer que le fichier intermédiaire n'arrive jamais sur le disque. Il vous suffit d'utiliser un shell avancé tel que ZSH (mon préféré) qui prend en charge la substitution de processus :

paste <( <input.txt awk "BEGIN { R=0; N=0; } /Header pattern/ { N=1; } { R=R+N; N=0; print R; }" ) input.txt | awk -f yourscript.awk 

Rendons ce one-liner plus adapté à l'exposition :

P="/Header pattern/"
X="BEGIN { R=0; N=0; } $P { N=1; } { R=R+N; N=0; print R; }"
paste <( <input.txt awk $X ) input.txt | awk -f yourscript.awk 

Cela démarre trois processus :le script AWK en ligne trivial, paste , et le script AWK que vous vouliez vraiment exécuter en premier lieu.

Dans les coulisses, le <() La construction de ligne de commande crée un canal nommé et transmet le nom du canal à coller comme nom de son premier fichier d'entrée. Pour paste du second fichier d'entrée, on lui donne le nom de notre fichier d'entrée d'origine (ce fichier est donc lu séquentiellement, en parallèle, par deux processus différents, qui en consommeront entre eux au plus un lu à partir du disque, si le fichier d'entrée est froid).

Le tube nommé magique au milieu est un FIFO en mémoire que l'ancien Unix gérait probablement à environ 16 Ko de taille moyenne (interrompant par intermittence le paste traiter si le yourscript.awk le processus est lent à vider ce FIFO vers le bas).

Peut-être que l'Unix moderne y ajoute un tampon plus grand parce qu'il le peut, mais ce n'est certainement pas une ressource rare dont vous devriez vous préoccuper, jusqu'à ce que vous écriviez votre premier truly ligne de commande avancée avec redirection de processus impliquant ceux-ci par centaines ou milliers :-)

Considérations supplémentaires sur les performances

Sur les processeurs modernes, ces trois processus pourraient facilement s'exécuter sur des cœurs séparés.

Les deux premiers de ces processus sont à la limite du trivial :un script AWK avec une seule correspondance de modèle et une comptabilité mineure, paste appelé avec deux arguments. yourscript.awk aura du mal à courir plus vite que ceux-ci.

Quoi, votre machine de développement n'a pas de cœurs légèrement chargés pour rendre ce modèle de solution maître-maître presque gratuit dans le domaine d'exécution ?

Ding Dong.

Bonjour?

Hé, c'est pour toi. 2018 vient d'appeler et veut récupérer son problème.

2020 est officiellement le sursis de MTV :c'est comme ça qu'on l'aime, des tubes magiques pour rien et des noyaux gratuits. Sans parler à haute voix d'un fournisseur de puces TLA particulier qui fait vibrer l'espace ces jours-ci.

En dernier lieu, si vous ne voulez pas la surcharge liée à l'analyse des numéros d'enregistrement réels :

X="BEGIN { N=0; } $P { N=1; } { print N; N=0; }"

Maintenant, votre fichier intermédiaire in-FIFO est annoté avec seulement deux caractères supplémentaires ajoutés à chaque ligne ('0' ou '1' et le caractère de séparation par défaut ajouté par paste ), avec '1' marquant la première ligne de l'enregistrement.

FIFO nommés

Sous le capot, ce ne sont pas différents des FIFO magiques instanciés par Unix lorsque vous écrivez une commande pipe normale :

cat file | proc1 | proc2 | proc2 

Trois canaux sans nom (et tout un processus consacré à cat vous n'en avez même pas eu besoin).

C'est presque dommage que le vraiment exceptionnel la commodité des flux stdin/stdout par défaut tels que prégéré par le shell masque la réalité que paste $magictemppipe1 $magictemppipe2 ne porte aucune considération de performance supplémentaire digne de réflexion, dans 99% des cas.

"Utilisez le <() Joint en Y, Luke."

Votre réflexe instinctif vers la décomposition sémantique naturelle dans le domaine du problème en bénéficiera énormément.

Si quelqu'un avait eu l'intelligence de nommer la construction du shell <() en tant qu'opérateur YODA en premier lieu, je soupçonne qu'il aurait été poussé au service universel il y a au moins une bonne décennie.


Cela peut être un moyen :

$ awk '$1=="BB" && $2>1 {print f} {f=$1}' file
AAAAAAAAAAAAA

Explication

  • $1=="BB" && $2>1 {print f} si le 1er champ est exactement BB et le 2ème champ est supérieur à 1 , puis imprimez f , une valeur stockée.
  • {f=$1} stocker la ligne courante dans f , pour qu'il soit accessible lors de la lecture de la ligne suivante.

Linux
  1. Exemples de commandes awk sous Linux

  2. awk :commande introuvable

  3. Imprimer la dernière ligne d'un fichier, à partir de la CLI

  4. commande cut ou awk pour imprimer le premier champ de la première ligne

  5. Comment imprimer la dernière ligne d'un fichier compressé gz dans la ligne de commande ?

Comment lire des fichiers ligne par ligne dans Bash

Comment lire un fichier ligne par ligne dans Bash

Imprimer deux fichiers sur deux colonnes ?

Imprimer la ligne précédente après une correspondance de modèle à l'aide de Sed ?

Boîte occupée lire le fichier ligne par ligne ?

Commande AWK sous Linux/Unix