¿Cómo funciona awk '! A ++'?

Este delineador único elimina líneas duplicadas de la input de text sin sorting previa.

Por ejemplo:

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

El código original que he encontrado en los internets decía:

awk '!_[$0]++'

Esto fue aún más desconcertante para mí, ya que tomé _ tener un significado especial en awk, como en Perl, pero resultó ser solo el nombre de una matriz.

Ahora, entiendo la lógica detrás del one-liner: cada línea de input se utiliza como una key en una matriz de hash, por lo tanto, una vez completada, el hash contiene líneas únicas en el order de llegada.

Lo que me gustaría aprender es cómo exactamente esta notación es interpretada por awk. Por ejemplo, qué significa el signo de bang ( ! ) Y los demás elementos de este fragment de código.

¿Como funciona?

Veamos,

  !a[$0]++ 

primero

  a[$0] 

observamos el valor de a[$0] (matriz a con línea de input completa ( $0 ) como key).

Si no existe ( ! Es la negación en la testing se evaluará a verdadero)

  !a[$0] 

imprimimos la línea de input $0 (acción pnetworkingeterminada).

Además, agregamos uno ( ++ ) a a[$0] , ¡así que la próxima vez !a[$0] se evaluará a falso.

¡Bien, encuentra! ¡Deberías echarle un vistazo al código de golf!

Aquí está el procesamiento:

  • a[$0] : mire el valor de la key $0 , en la matriz asociativa a . Si no existe, créelo.

  • a[$0]++ : incremente el valor de a[$0] , devuelva el valor anterior como valor de expresión. Si no existe a[$0] , devuelve 0 e incrementa a[$0] a 1 (el operador ++ devuelve el valor numérico).

  • !a[$0]++ : niega el valor de la expresión. Si a[$0]++ devuelve 0 , la expresión completa se evalúa como verdadera, hace que awk realice la acción pnetworkingeterminada print $0 . De lo contrario, toda la expresión se evalúa como falsa, provoca que awk no haga nada.

Referencias

  • Expresión en awk
  • gawk – Operadores de incremento y decremento

Con gawk , podemos usar dgawk (o awk --debug con la versión más nueva) para depurar un script de gawk . Primero, crea un script de gawk , llamado test.awk :

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

Entonces corre:

 dgawk -f test.awk 

o:

 gawk --debug -f test.awk 

En la console del depurador:

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

Como puedes ver, Op_postincrement se ejecutó antes Op_not .

También puede usar si o stepi lugar de s o step para ver más claramente:

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