Come funziona awk '! A [$ 0] ++'?


40

Questo one-liner rimuove le righe duplicate dall'inserimento del testo senza pre-ordinamento.

Per esempio:

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

Il codice originale che ho trovato su Internet recita:

awk '!_[$0]++'

Questo è stato ancora più sconcertante per me dato che ho assunto _un significato speciale in awk, come in Perl, ma si è rivelato essere solo un nome di un array.

Ora capisco la logica dietro il one-liner: ogni riga di input viene utilizzata come chiave in un array di hash, quindi, al termine, l'hash contiene righe uniche nell'ordine di arrivo.

Quello che vorrei imparare è come esattamente questa notazione è interpretata da awk. Ad esempio, ciò che significa il segno di botto ( !) e gli altri elementi di questo frammento di codice.

Come funziona?


il titolo è fuorviante, dovrebbe essere $ 0 (zero), non $ o (o).
Archemar,

2
Essendo un hash, non è ordinato, quindi "nell'ordine di arrivo" non è in realtà corretto.
Kevin,

Risposte:


35

Vediamo,

 !a[$0]++

primo

 a[$0]

esaminiamo il valore di a[$0](matrice acon l'intera riga di input ( $0) come chiave).

Se non esiste (la !negazione in test sarà valutata vera)

 !a[$0]

stampiamo la riga di input $0(azione predefinita).

Inoltre, aggiungiamo uno ( ++) a a[$0], quindi la prossima volta !a[$0]verrà valutato falso.

Bello, trova !! Dovresti dare un'occhiata al codice golf!


1
Quindi l'essenza è questa: l'espressione tra virgolette singole viene utilizzata awkcome test per ogni riga di input; ogni volta che il test ha awkesito positivo esegue l'azione tra parentesi graffe, che, se omesso, lo è {print}. Grazie!
Alexander Shcheblikin,

3
@Archemar: questa risposta è sbagliata, vedi la mia.
cuonglm,

@AlexanderShcheblikin in awk, l'azione predefinita è {print $0}. Ciò significa che tutto ciò che viene valutato come vero lo eseguirà come predefinito. Quindi, ad esempio, awk '1' filestampa tutte le linee, awk '$1' filestampa tutte quelle linee il cui primo campo non è vuoto o 0, ecc.
fedorqui,

6
@Gnouc Non vedo alcun errore grave in questa risposta. Se questo è ciò a cui ti riferisci, l'incremento viene effettivamente applicato dopo il calcolo del valore dell'espressione. È vero che l'incremento avviene prima della stampa, ma si tratta di un'imprecisione minore che non influisce sulla spiegazione di base.
Gilles 'SO- smetti di essere malvagio' il

1
Ho trovato la migliore spiegazione per un principiante da capire qui in quora: qr.ae/TUIVxM
GP92

30

Ecco l'elaborazione:

  • a[$0]: guarda il valore della chiave $0, nell'array associativo a. Se non esiste, crearlo.

  • a[$0]++: incrementa il valore di a[$0], restituisce il vecchio valore come valore dell'espressione. Se a[$0]non esiste, ritorna 0e incrementa a[$0]su 1(l' ++operatore restituisce un valore numerico).

  • !a[$0]++: nega il valore dell'espressione. Se a[$0]++restituito 0, l'intera espressione viene valutata su true, awkeseguire l'azione predefinita eseguita print $0. Altrimenti, l'intera espressione viene valutata come falsa, le cause awknon fanno nulla.

Riferimenti:

Con gawk, possiamo usare dgawk (o awk --debugcon la versione più recente) per eseguire il debug di uno gawkscript. Innanzitutto, crea uno gawkscript, chiamato test.awk:

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

Quindi eseguire:

dgawk -f test.awk

o:

gawk --debug -f test.awk

Nella console di debugger:

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

Si può vedere, è Op_postincrementstato eseguito prima Op_not.

Puoi anche usare sio stepiinvece di so stepper vedere più chiaramente:

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++;

3
@Archemar: la tua risposta indica che !è stata applicata prima ++.
cuonglm,

6
Questa risposta è sbagliata L'incremento avviene dopo il !calcolo del risultato dell'operatore. Stai confondendo la precedenza dell'operatore ( !a[$0]++analizzata come !(a[$0]++)) con l'ordine di valutazione (l'assegnazione del nuovo valore di a[$0]avviene dopo che il valore dell'espressione è stato calcolato).
Gilles 'SO- smetti di essere malvagio' il

5
@Gnouc Dice proprio nel passaggio che hai citato, e se funzionasse come descritto, questo codice non avrebbe l'effetto desiderato. Innanzitutto !xviene calcolato il valore , dove xè il vecchio valore di a[$0]. Quindi a[$0]è impostato su 1+x.
Gilles 'SO- smetti di essere malvagio' il

7
Credo che la tua analisi di ciò che fa Awk sia corretta. Scusate se ieri ho insinuato diversamente. Tuttavia, la tua critica alla risposta di Archemar è sbagliata. Archemar non fraintende la precedenza, sì, stai confondendo la precedenza con l'ordine di valutazione (vedi il mio commento precedente). Se rimuovi qualsiasi menzione della risposta di Archemar nella tua, la tua risposta dovrebbe essere corretta. Allo stato attuale, si concentra sulla dimostrazione errata di Archemar, e non è così.
Gilles 'SO- smetti di essere malvagio'

5
bene, almeno ora so del debugger di Awk ...
Archemar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.