Comment fonctionne awk '! A ++'?

Cette ligne unique supprime les lignes en double de l'input de text sans pré-sorting.

Par exemple:

$ cat >f q w e w r $ awk '!a[$0]++' <f q w e r $ 

Le code original que j'ai trouvé sur les internets se lit comme suit:

awk '!_[$0]++'

C'était encore plus perplexe pour moi car j'ai pris _ pour avoir un sens spécial dans awk, comme dans Perl, mais il s'est avéré être juste un nom d'un tableau.

Maintenant, je comprends la logique derrière le one-liner: chaque ligne d'input est utilisée comme key dans un tableau de hachage, ainsi, à la fin, le hachage contient des lignes uniques dans l'ordre d'arrivée.

Ce que je voudrais apprendre, c'est comment exactement cette notation est interprétée par awk. Par exemple ce que le signe bang ( ! ) Signifie et les autres éléments de cet extrait de code.

Comment ça marche?

    Voyons voir,

      !a[$0]++ 

    premier

      a[$0] 

    nous regardons la valeur d' a[$0] (tableau a avec ligne d'input entière ( $0 ) comme key).

    S'il n'existe pas ( ! Est la négation dans le test sera eval à vrai)

      !a[$0] 

    nous imprimons la ligne d'input $0 (action par défaut).

    En outre, nous ajoutons un ( ++ ) à a[$0] , donc la prochaine fois !a[$0] sera évalué à false.

    Nice, trouvez !! Vous devriez jeter un oeil au code golf!

    Voici le traitement:

    • a[$0] : regarde la valeur de la key $0 , dans le tableau associatif a . S'il n'existe pas, créez-le.

    • a[$0]++ : incrémente la valeur d' a[$0] , renvoie l'ancienne valeur comme valeur de l'expression. Si a[$0] n'existe pas, renvoyez 0 et incrémentez a[$0] à 1 (l'opérateur ++ returnne la valeur numérique).

    • !a[$0]++ : annule la valeur de l'expression. Si a[$0]++ renvoie 0 , l'expression entière est évaluée à true, make awk exécuté l'action par défaut print $0 . Sinon, l'expression entière est évaluée à false, ce qui fait que awk ne fait rien.

    Les references:

    • Expression dans awk
    • gawk – Opérateurs d'incrémentation et de décrémentation

    Avec gawk , nous pouvons utiliser dgawk (ou awk --debug avec une version plus récente) pour déboguer un script gawk . Tout d'abord, créez un script gawk , nommé test.awk :

     BEGIN { a = 0; !a++; } 

    Puis exécutez:

     dgawk -f test.awk 

    ou:

     gawk --debug -f test.awk 

    Dans la console du débogueur:

     $ dgawk -f test.awk dgawk> trace on dgawk> watch a Watchpoint 1: a dgawk> run Starting program: [ 1:0x7fe59154cfe0] Op_rule : [in_rule = BEGIN] [source_file = test.awk] [ 2:0x7fe59154bf80] Op_push_i : 0 [PERM|NUMCUR|NUMBER] [ 2:0x7fe59154bf20] Op_store_var : a [do_reference = FALSE] [ 3:0x7fe59154bf60] Op_push_lhs : a [do_reference = TRUE] Stopping in BEGIN ... Watchpoint 1: a Old value: untyped variable New value: 0 main() at `test.awk':3 3 !a++; dgawk> step [ 3:0x7fe59154bfc0] Op_postincrement : [ 3:0x7fe59154bf40] Op_not : Watchpoint 1: a Old value: 0 New value: 1 main() at `test.awk':3 3 !a++; dgawk> 

    Vous pouvez voir, Op_postincrement été exécuté avant Op_not .

    Vous pouvez également utiliser si ou stepi au lieu de s ou step pour voir plus clairement:

     dgawk> si [ 3:0x7ff061ac1fc0] Op_postincrement : 3 !a++; dgawk> si [ 3:0x7ff061ac1f40] Op_not : Watchpoint 1: a Old value: 0 New value: 1 main() at `test.awk':3 3 !a++;