Por que o awk split () faz o primeiro campo ser o último elemento da matriz?

1

Provavelmente estou sentindo falta de algo realmente simples aqui, mas quando digo

echo 'The quick brown fox jumped over the lazy dog.' | \
    awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print $WORD;
        }
    }'

Eu recebo isso em troca:

quick
brown
fox
jumped
over
the
lazy
dog.
The

Por que a primeira palavra é impressa por último?

$ awk --version
awk version 20070501
    
por Rhymoid 01.08.2015 / 13:43

3 respostas

6

Em primeiro lugar, for (i in array) in awk produz o índice da matriz, não os elementos da matriz. Então você obteve o resultado como se tivesse acessado $1 . $2 ... $NF .

echo 'The quick brown fox jumped over the lazy dog.' | \
    awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print WORD;       
        }
    }'
2
3
4
5
6
7
8
9
1

Você pode ver que tem índices de matriz ao acessar a variável WORD .

Para sua pergunta, o POSIX definiu o looping por awk array, produzindo o índice de matriz em pedido não especificado :

for (variable in array)

which shall iterate, assigning each index of array to variable in an unspecified order.

Portanto, cabe à implementação definir como percorrer a matriz.

Um teste rápido no meu sistema mostrou que gawk e mawk estão em loop com ordem crescente:

for AWK in gawk mawk /usr/5bin/[on]awk /usr/5bin/posix/awk; do
  printf '==%s==\n' "$AWK"
  echo 'The quick brown fox jumped over the lazy dog.' |
  "$AWK" '{
    split($0, WORDS, " ")
    for (WORD in WORDS) {
      print WORD;
    }
  }' | { sed 1q; tail -n1 }
 done
==awk==
1
9
==mawk==
1
9
==/usr/5bin/nawk==
2
1
==/usr/5bin/oawk==
2
1
==/usr/5bin/posix/awk==
2
1

(Com GNU sed , você precisa de sed -u 1q )

    
por 01.08.2015 / 13:58
5

Você não está imprimindo os elementos do array, está imprimindo os campos em ordem. Em awk , as variáveis não são prefixadas com $ , ou seja, campos. Portanto, $a imprimirá o campo de qualquer número armazenado em a . Para imprimir uma variável, digamos foo , você precisa de print foo , no $ .

Quando você faz uma iteração em uma matriz awk , você está interagindo com os índices da matriz:

$ echo 'The quick brown fox jumped over the lazy dog.' |     awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print WORD;
        }
    }'
1
2
3
4
5
6
7
8
9

O que você procurava era:

$ echo 'The quick brown fox jumped over the lazy dog.' |     awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print WORDS[WORD];
        }
    }'
The
quick
brown
fox
jumped
over
the
lazy
dog.

Qual, no GNU awk , é equivalente a:

 $ echo 'The quick brown fox jumped over the lazy dog.' |     awk '{
            for (i=1; i<=NF;i++){
            print $i
        }
    }'

Enquanto gawk (GNU awk ) split classificará a matriz na ordem em que foi encontrada (como mostrado acima), outras implementações não fazem isso como explica o cuonglm em sua resposta. Portanto, em vez de usar split , você pode definir o separador de campo e deixar awk na divisão. No seu exemplo, não há necessidade, pois o separador é um espaço, mas veja como fazer isso em outros casos:

 $ echo 'The-quick-brown-fox-jumped-over-the-lazy-dog.' | 
    awk -F"-" '{
                 for(i=1;i<=NF;i++){
                    print $i
                 }
                }'
The
quick
brown
fox
jumped
over
the
lazy
dog.
    
por 01.08.2015 / 14:15
3

Ignorando o fato de que, em seu exemplo, você poderia (deve) imprimir apenas cada campo $1 , $2 , etc., split retorna o número de elementos na matriz, portanto, para percorrê-los no Para que eles aparecessem, você pode usar algo assim:

echo 'The quick brown fox jumped over the lazy dog.' | \
    awk '{
        n = split($0, WORDS, " ");
        for (i = 1; i <= n; ++i) {
            print WORDS[i];
        }
    }'

Como outros já mencionaram, a ordem de travessia através de um array ao usar for (indx in array) não é especificada (embora você possa controlá-lo se estiver usando o GNU awk).

    
por 01.08.2015 / 18:08

Tags