Lors de l'utilisation de awk / pattern / {print "text"} / patern / {print ""}, existe-t-il un autre motif?

22

Disons que j'ai un fichier texte comme:

R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

Je veux utiliser awkpour traiter ces lignes différemment, comme

awk '/R1/ { print "=>" $0} /R2/ { print "*" $0} '

et je veux également imprimer toutes les autres lignes telles quelles (sans faire de doublons des lignes que j'ai déjà traitées), en gros j'ai besoin d'un /ELSE/ { print $0}à la fin de ma awkligne.

Existe-t-il une telle chose?

Ali
la source

Réponses:

27

Approche simplifiée avec awk

awk '/R1/ {print "=>" $0;next} /R2/{print "*" $0;next} 1' text.file

[jaypal:~/Temp] cat text.file 
R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

[jaypal:~/Temp] awk '/R1/ { print "=>" $0;next} /R2/{print "*" $0;next}1' text.file
=>R1 12 324 3453 36 457 4 7 8
*R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242
[jaypal:~/Temp] 

Répartition des instructions de modèle {action}:

  • /R1/ { print "=>" $0;next}: Cela signifie que les lignes ayant /R1/l'action d'impression =>seront effectuées. nextsignifie que le reste des instructions awk sera ignoré et la ligne suivante sera examinée.

  • /R2/{print "*" $0;next}: Cela signifie que les lignes correspondant à l' pattern /R2/action d'impression *seront effectuées. Lorsque le awktraitement démarre, la première pattern {action}instruction sera ignorée car pattern /R1/elle ne sera pas vraie pour les lignes ayant /R2/. Donc, la deuxième pattern {action}déclaration se fera sur la ligne. nextsignifierait à nouveau que nous ne voulons plus de traitement et awkirons dûment à la ligne suivante.

  • 1imprime toutes les lignes. Quand juste une condition est fournie sans {action}, awk utilise par défaut {print}. Ici, la condition est 1interprétée comme vraie, donc elle réussit toujours. Si nous arrivons à ce point, c'est parce que les première et deuxième pattern {action}instructions ont été ignorées ou contournées (pour les lignes ne contenant pas /R1/et /R2/), donc l'action d'impression par défaut sera effectuée pour les lignes restantes.

jaypal singh
la source
Semble marginalement exécuter le plus rapidement de toutes les solutions publiées.
Chris Down
1
Je ne suis pas sûr que le sucre syntaxique soit le bon terme ici ... C'est juste de la syntaxe.
Daniel Hershcovich
7

awkmet en œuvre les suspects habituels en matière de conditionnels. C'est une bonne idée d'utiliser printfau lieu de printpour le travail que vous souhaitez faire en match.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'
Chris Down
la source
Vous n'en avez pas vraiment besoin if-then-else.
jaypal singh
1
Bien que cela fonctionne parfaitement bien, ce n'est pas idiomatique. L'utilisation judicieuse de nextest un outil important dans la programmation awk.
dmckee
2
Je ne comprends pas l'intérêt d'utiliser printfici. Son seul avantage (sauf si vous faites un formatage plus sophistiqué que la concaténation) est qu'il n'ajoute pas de nouvelle ligne, ce qui n'est pas pertinent ici.
Gilles 'SO- arrête d'être méchant'
1
C'est un résultat contre-intuitif et surprenant. Unorned printn'a qu'à sortir $0alors qu'il printfdoit analyser une chaîne de format.
jw013
5

Chris Down a déjà montré comment obtenir un autre pour les expressions rationnelles en utilisant une instruction explicite «if» dans un bloc. Vous pouvez également obtenir le même effet d'une autre manière, bien que sa solution soit probablement meilleure.

La première consiste à écrire une troisième expression régulière qui ne correspondra qu'au texte qui ne correspond pas aux autres, dans votre cas, cela ressemblerait à ceci:

awk '/^R1/ { print "=>" $0}
     /^R2/ { print "*" $0}
     /^[^R]/ || /^R[^12]/ { print $0 } '

Remarque, cela utilise des expressions rationnelles ancrées - le ^ au début des expressions régulières ne correspondra qu'au début d'une ligne - vos modèles originaux ne l'ont pas fait, ce qui ralentit légèrement la correspondance car il vérifiera tous les caractères sur une ligne plutôt que sauter jusqu'à la ligne suivante. Le troisième cas ("else") correspondra à une ligne qui commence par un caractère qui n'est pas 'R' ([^ R]) ou qui commence par un 'R' suivi d'un caractère qui n'est pas un '1' ou ' 2 '(R [^ 12]). Les deux significations différentes de ^ sont quelque peu déroutantes, mais cette erreur a été commise il y a longtemps et ne sera pas modifiée de si tôt.

Pour utiliser des expressions rationnelles complémentaires, elles doivent vraiment être ancrées, sinon le [^ R] correspondrait par exemple au 1 qui le suit. Pour les regexps très simples comme vous, cette approche peut être utile, mais à mesure que les regexps deviennent plus complexes, cette approche deviendra ingérable. Au lieu de cela, vous pouvez utiliser des variables d'état pour chaque ligne, comme ceci:

awk '{ handled = 0 }
     /^R1/ { print "=>" $0; handled = 1}
     /^R2/ { print "*" $0; handled = 1}
     { if (!handled) print $0 } '

Cela met à zéro géré pour chaque nouvelle ligne, puis à 1 si elle correspond à l'un des deux regexps, et enfin, si elle est toujours nulle, exécute l'impression $ 0.

Alex Dupuy
la source
Il convient de noter que sur les fichiers volumineux, les deux sont moins efficaces que l'utilisation de conditions (comme illustré ici ). rfilen'est que 10000 lignes de l'ensemble de données du questionneur répétées.
Chris Down
4
if (!handled)Beurk! Utilisez nextpour arrêter d'envisager d'autres actions.
dmckee
+1 pour if (!handled). Les solutions générales, flexibles et réutilisables sont bonnes. Que se passe-t-il si la prochaine personne qui a cette question souhaite faire plus de traitement après l'impression? Les réponses avec nextne soutiennent pas cela.
Scott