web-dev-qa-db-fra.com

Comment fonctionne Awk '! A [$ 0] ++' travail?

Cette doublure supprime des lignes en double à partir de la saisie de texte sans pré-tri.

Par exemple:

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

Le code d'origine que j'ai trouvé sur les internets a lu:

awk '!_[$0]++'

C'était encore plus perplexe pour moi alors que j'ai pris _ Pour avoir une signification particulière dans Awk, comme dans Perl, mais il s'est avéré être juste un nom d'une matrice.

Maintenant, je comprends la logique derrière la ligne one-liner: Chaque ligne d'entrée est utilisée comme une clé dans une matrice de hachage. Ainsi, à la fin, le hachage contient des lignes uniques dans l'ordre d'arrivée.

Ce que j'aimerais apprendre, c'est comment exactement cette notation est interprétée par Awk. Par exemple. Quel signe de bang (!) signifie et les autres éléments de cet extrait de code.

Comment ça marche?

42

Voyons,

 !a[$0]++

premier

 a[$0]

nous examinons la valeur de a[$0] (Array a avec une ligne d'entrée entière ($0) comme clé).

Si cela n'existe pas (! Est-ce que la négation dans le test sera évaluée à la vraie)

 !a[$0]

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

En outre, nous en ajoutons un (++ ) à a[$0], Alors la prochaine fois !a[$0] évaluera à faux.

Nice, trouver !! Vous devriez avoir un coup d'œil au code golf!

37
Archemar

Voici le traitement:

  • a[$0]: regardez la valeur de la clé $0, dans le tableau associatif a. Si cela n'existe pas, créez-le.

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

  • !a[$0]++: nier la valeur de l'expression. Si a[$0]++ revenir 0, l'ensemble de l'expression est évalué à vrai, faire awk action par défaut print $0. Sinon, toute l'expression est évaluée à FALSE, causes awk ne rien faire.

Les références:

Avec gawk, nous pouvons utiliser dgawk (ou awk --debug avec la 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 de 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>

Tu peux voir, Op_postincrement a é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++;
30
cuonglm