Como o awk '! a [$ 0] ++' funciona?

32

Este one-liner remove linhas duplicadas da entrada de texto sem pré-ordenação.

Por exemplo:

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

O código original que encontrei nas internets dizia:

awk '!_[$0]++'

Isso foi ainda mais desconcertante para mim, pois usei _ para ter um significado especial no awk, como em Perl, mas acabou sendo apenas um nome de uma matriz.

Agora, eu entendo a lógica por trás do one-liner: cada linha de entrada é usada como chave em um array de hash, assim, após a conclusão, o hash contém linhas exclusivas na ordem de chegada.

O que eu gostaria de aprender é exatamente como essa notação é interpretada pelo awk. Por exemplo. o que significa o sinal de estrondo ( ! ) e os outros elementos deste trecho de código.

Como funciona?

    
por Alexander Shcheblikin 06.10.2014 / 22:56

2 respostas

30

Vamos ver,

 !a[$0]++

primeiro

 a[$0]

olhamos o valor de a[$0] (array a com toda a linha de entrada ( $0 ) como chave).

Se não existir ( ! é negação no teste, o resultado será verdadeiro)

 !a[$0]

nós imprimimos a linha de entrada $0 (ação padrão).

Além disso, adicionamos um ( ++ ) a a[$0] , então da próxima vez que !a[$0] será avaliado como falso.

Nice, encontre !! Você deveria dar uma olhada no code golf!

    
por 06.10.2014 / 23:03
26

Aqui está o processamento:

  • a[$0] : observe o valor da chave $0 , na matriz associativa a . Se não existir, crie-o.

  • a[$0]++ : incrementa o valor de a[$0] , retorna o valor antigo como valor da expressão. Se a[$0] não existir, retorne 0 e incremento a[$0] a 1 ( ++ operator retorna valor numérico).

  • !a[$0]++ : nega o valor da expressão. Se a[$0]++ retornar 0 , a expressão inteira será avaliada como true, fazer awk executar a ação padrão print $0 . Caso contrário, a expressão inteira será avaliada como falsa, fazendo com que awk não faça nada.

Referências:

Com gawk , podemos usar dgawk (ou awk --debug com a versão mais recente) para depurar um script gawk . Primeiro, crie um script gawk , denominado test.awk :

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

Em seguida, execute:

dgawk -f test.awk

ou:

gawk --debug -f test.awk

Na consola do 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>

Você pode ver que Op_postincrement foi executado antes de Op_not .

Você também pode usar si ou stepi em vez de s ou step para ver com mais clareza:

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++;
    
por 07.10.2014 / 04:02