Wie funktioniert awk '! A [$ 0] ++'?

39

Dieser Einzeiler entfernt doppelte Zeilen aus der Texteingabe ohne Vorsortierung.

Beispielsweise:

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

Der Originalcode, den ich auf den Internets gefunden habe, lautete:

awk '!_[$0]++'

Dies war für mich noch verwirrender, als ich _eine besondere Bedeutung in awk annahm, wie in Perl, aber es stellte sich heraus, dass es sich nur um den Namen eines Arrays handelte.

Jetzt verstehe ich die Logik hinter dem Einzeiler : Jede Eingabezeile wird als Schlüssel in einem Hash-Array verwendet, daher enthält der Hash nach Fertigstellung eindeutige Zeilen in der Reihenfolge des Eintreffens.

Was ich lernen möchte, ist, wie genau diese Notation von awk interpretiert wird. ZB was das Bang-Zeichen ( !) bedeutet und die anderen Elemente dieses Code-Snippets.

Wie funktioniert es?

Alexander Shcheblikin
quelle
Titel ist irreführend, es sollte $ 0 (Null) sein, nicht $ o (o).
Archemar
2
Da es sich um ein Hash handelt, ist es ungeordnet, sodass "in der Reihenfolge der Ankunft" nicht richtig ist.
Kevin

Antworten:

35

Wir werden sehen,

 !a[$0]++

zuerst

 a[$0]

Wir betrachten den Wert von a[$0](Array amit der ganzen Eingabezeile ( $0) als Schlüssel).

Wenn es nicht existiert ( !Negation im Test wird zu true ausgewertet)

 !a[$0]

Wir drucken die Eingabezeile $0(Standardaktion).

Außerdem fügen wir one ( ++) hinzu a[$0], damit beim nächsten Mal !a[$0]false ausgewertet wird.

Schön zu finden !! Sie sollten sich Codegolf ansehen!

Archemar
quelle
1
Das Wesentliche ist also Folgendes: Der Ausdruck in den einfachen Anführungszeichen wird awkals Test für jede Eingabezeile verwendet. Bei jedem erfolgreichen Test awkwird die Aktion in geschweiften Klammern ausgeführt, die weggelassen werden {print}. Vielen Dank!
Alexander Shcheblikin
3
@Archemar: Diese Antwort ist falsch, siehe meine.
cuonglm
@AlexanderShcheblikin in awk, ist die Standardaktion {print $0}. Dies bedeutet, dass alles, was als wahr bewertet wird, dies standardmäßig ausführt. So werden zum Beispiel awk '1' filealle Zeilen awk '$1' filegedruckt, alle Zeilen, deren erstes Feld nicht leer oder 0 ist, usw.
fedorqui
6
@ Gnouc Ich sehe keinen schwerwiegenden Fehler in dieser Antwort. Wenn Sie sich auf diese beziehen, wird die Inkrementierung tatsächlich angewendet, nachdem der Wert des Ausdrucks berechnet wurde. Es ist wahr, dass die Inkrementierung vor dem Drucken erfolgt, aber das ist eine geringfügige Ungenauigkeit, die die grundlegende Erklärung nicht beeinflusst.
Gilles 'SO- hör auf böse zu sein'
1
Ich fand die beste Erklärung, die ein Neuling hier in Quora verstehen kann: qr.ae/TUIVxM
GP92
29

Hier ist die Bearbeitung:

  • a[$0]: Schauen Sie sich den Wert des Schlüssels $0im assoziativen Array an a. Wenn es nicht vorhanden ist, erstellen Sie es.

  • a[$0]++: Inkrementiere den Wert von a[$0], gib den alten Wert als Ausdruckswert zurück. Ist a[$0]dies nicht der Fall, kehren Sie zurück 0und erhöhen Sie a[$0]den Wert auf 1( ++Operator gibt numerischen Wert zurück).

  • !a[$0]++: negiere den Wert des Ausdrucks. Bei a[$0]++return 0wird der gesamte Ausdruck als true ausgewertet. Führen Sie awkeine Standardaktion aus print $0. Andernfalls wird der gesamte Ausdruck als falsch ausgewertet, und die Ursachen awktun nichts.

Verweise:

Mit gawkkönnen wir dgawk (oder eine awk --debugneuere Version) verwenden , um ein gawkSkript zu debuggen . Erstellen Sie zunächst ein gawkSkript mit dem Namen test.awk:

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

Dann renne:

dgawk -f test.awk

oder:

gawk --debug -f test.awk

In der Debugger-Konsole:

$ 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>

Sie sehen, Op_postincrementwurde vorher ausgeführt Op_not.

Sie können auch sioder stepianstelle von soder verwenden step, um Folgendes deutlicher zu sehen:

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++;
cuonglm
quelle
3
@Archemar: Deine Antwort zeigt an, dass das schon mal !angewendet wurde ++.
Cuonglm
6
Diese Antwort ist falsch. Die Inkrementierung erfolgt, nachdem das Ergebnis des !Operators berechnet wurde. Sie verwechseln die Priorität eines Operators ( !a[$0]++wird wie folgt analysiert !(a[$0]++)) mit der Reihenfolge der Auswertung (die Zuweisung des neuen Werts von a[$0]erfolgt, nachdem der Wert des Ausdrucks berechnet wurde).
Gilles 'SO- hör auf böse zu sein'
5
@ Gnouc In der von Ihnen zitierten Passage steht, und wenn es so funktioniert hätte, wie Sie es beschrieben haben, hätte dieser Code nicht den gewünschten Effekt. Zunächst wird der Wert !xberechnet, wobei xder alte Wert von ist a[$0]. Dann a[$0]steht auf 1+x.
Gilles 'SO- hör auf böse zu sein'
7
Ich glaube, dass Ihre Analyse, was awk tut, richtig ist. Entschuldigung, wenn ich gestern etwas anderes angedeutet habe. Ihre Kritik an Archemars Antwort ist jedoch falsch. Archemar versteht die Rangfolge nicht falsch, Sie verwechseln die Rangfolge mit der Reihenfolge der Bewertung (siehe meinen vorherigen Kommentar). Wenn Sie die Erwähnung von Archemars Antwort in Ihrer entfernen, sollte Ihre Antwort korrekt sein. So wie es ist, konzentriert es sich darauf, Archemar das Gegenteil zu beweisen, und dies ist nicht der Fall.
Gilles 'SO - hör auf böse zu sein'
5
Nun, zumindest weiß ich jetzt etwas über den Debugger von awk ...
Archemar