1
votes

Sous-ensemble basé sur un fichier sur une ligne et impression d'autres lignes jusqu'à ce qu'il atteigne la ligne suivante

J'ai un fichier texte qui contient plusieurs entrées qui ressemblent à l'exemple ci-dessous:

# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S

Je dois trouver un moyen, de préférence en Bash ou Perl, qui lit la ligne avec le # , sous-ensembles cette ligne en fonction de la colonne 8 (latitude), et si la condition est remplie, imprime le reste des lignes (par exemple LM ...) jusqu'à ce qu'il atteigne la ligne suivante avec le # . Par exemple, je veux seulement imprimer les «entrées» où la colonne 8

Je peux trouver du code pour lire chaque ligne # , mais je ne sais pas comment programmer "si la condition est remplie, imprimez les lignes LM jusqu'à ce que vous atteigniez la ligne # suivante ". Le résultat attendu serait:

# 2018 11 21 17 47 37.708756 -34.390213 116.803673 2.6972 0.442474 3.324627 2.840390 0.885880 890
LM01 0.836408 1.00 P
LM01 1.035398 1.00 S
LM03 3.987074 1.00 S
# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S


2 commentaires

Souhaitez-vous s'il vous plaît montrer la sortie attendue en fonction de l'échantillon donné?


Bien sûr, j'ai fourni la sortie attendue ci-dessus - un sous-ensemble basé sur la colonne 8 de la ligne # étant <-34.4. Merci!


4 Réponses :


3
votes
perl -lane '$matches = ($F[7] < -34.4); print if ($matches .. (/^#/ and not $matches)) and ($matches or not /^#/)'
It's a bit involved. You can make $matches be any expression on the # ... lines that you want. ($matches .. (/^#/ and not $matches)) matches all header lines up to and including the next (potentially non-matching) header and then and ($matches or not /^#/) excludes any non-matching headers.(The .. is the Range Operator which was designed for exactly these use cases)

0 commentaires

2
votes

avec le séparateur d'enregistrements gawk , perl devrait avoir un résultat similaire ... code> puisque le signe est négatif. Puisque nous utilisons # comme délimiteur d'enregistrement, le numéro de champ est un de moins.

Nous définissons le séparateur d'enregistrement comme le premier # ou après une nouvelle ligne. Normalement, RS est entre les disques, mais ici, il mène les records. C'est pourquoi nous capturons le séparateur d'enregistrement correspondant RT et attribuons à une variable à utiliser dans l'enregistrement (suivant). Le RT inclut également la nouvelle ligne, c'est pourquoi printf n'en a pas.


1 commentaires

+1. Merci! Je me demandais si vous pouviez décrire ce que fait ce code? Je suis assez nouveau dans awk et j'espère que ce post pourra être utile à d'autres. Avec votre ligne unique ci-dessus, le séparateur d'enregistrement RS = '(^ | \ n) #' divise l'enregistrement, puis le reste de la ligne unique décrit la condition à imprimer pour chaque record? Merci!



5
votes

Sur les lignes ne commençant pas par # , imprimez si l'indicateur est activé, sinon définissez l'indicateur (et imprimez) sous une condition

perl -wlnE'
    if (/^\s*[^#]/) { say if $y } elsif ((split)[7] < -34.4) { $y=1, say }
' file

Avec l'exemple d'entrée proposé dans fichier ceci imprime la sortie attendue.

Les indicateurs -lnE peuvent être -ne à la place, avec print au lieu de say dans le code. Le -w est juste pour les avertissements, souvent omis dans les one-liners (je l'utilise toujours). Voir Commutateurs de commande dans perlrun


0 commentaires

1
votes

Un autre one-liner Perl

$ cat geeb.txt
# 2018 11 21 17 47 37.708756 -34.390213 116.803673 2.6972 0.442474 3.324627 2.840390 0.885880 890
LM01 0.836408 1.00 P
LM01 1.035398 1.00 S
LM03 3.987074 1.00 S
# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S
$ perl -0777 -ne ' while( /(^#.+?)(?=^#|\Z)/gsm ) { print $1 if (split(" ",$1))[7] < -34.4 } ' geeb.txt
# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S
$

avec des entrées

  perl -0777 -ne ' while( /(^#.+?)(?=^#|\Z)/gsm ) { print $1 if (split(" ",$1))[7] < -34.4 } '


3 commentaires

Joli :). Pas besoin de devis, il suffit de imprimer $ 1 . De plus, je trouve souvent plus clair de supprimer au moins quelques parenthèses, jusqu'à ... if (split "", $ 1) [7] <-34.4 (mais ce sont des préférences je suppose)


Merci @zdim. J'ai raccourci encore ... j'ai supprimé les parenthèses et utilisé $ & ..


OK, c'est une utilisation valide et intéressante de $ & mais je ne suis pas sûr que ce soit mieux que de capturer explicitement (cela peut causer des éraflures, peut être moins efficace ... et éventuellement introduire des bogues si les choses changent dans le futur)?