Como o 'getline' funciona no AWK?

1

Eu coloquei um exemplo usando a função AWK getline e está me confundindo.

$ cat in
foo
bar
baz
$ awk '{ getline tmp; print tmp; print $0 }' in
bar
foo
bar
baz

Estou lendo a próxima linha em uma variável chamada tmp , que não altera $0 , como confirmado pelas duas primeiras linhas de saída:

bar
foo

Isso é confirmado pela seguinte tabela tirada de A linguagem de programação AWK na página 62:

Eu sei que os componentes internos NR e FNR representam o número de linhas lidas até o momento. Acho que esta é a chave para entender o que está acontecendo, mas estou confuso como a alteração do NR em um passe afeta os passes futuros.

Eu esperava que as próximas duas linhas fossem:

baz
bar

porque na segunda passagem, $0 == bar e tmp == baz .

Então eu esperava que as próximas duas linhas realmente fossem apenas uma linha:

baz

porque na terceira passagem $0 == baz e tmp == null .

Portanto, minha saída esperada é:

bar
foo
baz
bar
baz

Acho que entender como alterar o NR no loop do awk é a chave para entender essa saída.

  • Você pode explicar por que minha saída esperada está errada e porque a saída real está correta?

Estou executando awk version 20070501 on macOS 10.12.1

    
por mbigras 26.05.2017 / 21:14

2 respostas

2

Acho que o que está faltando é que, ao definir NR , getline em efeito consome a linha. Então, na segunda chamada, bar já passou e $0 é baz ; getline tenta ler outra linha e falha; e o valor de tmp permanece inalterado (ou seja, igual a bar ).

Pode ser mais fácil de entender se você verificar o valor de retorno de getline :

awk '{ if ((getline tmp) > 0) print tmp; print $0 }' in
bar
foo
baz
    
por 26.05.2017 / 21:47
1

Deve ficar claro se você olhar para a foto maior, por assim dizer. Um programa awk é um loop em torno do texto do programa, que lê uma linha e executa o programa nessa linha. Se você ler uma linha dentro do programa, o loop circundante não conseguirá ver essa linha: ela já foi consumida.

Por exemplo, seu programa

{ getline tmp; print tmp; print $0 }

poderia ser escrito como

BEGIN {
    while (getline $0) {
        getline tmp; print tmp; print $0
    }
}

O bloco BEGIN é executado uma vez no início do programa, e aqui o programa não faz mais nada - claro que essa é uma maneira altamente não-idiomática de escrever código awk.

Aqui deve ficar claro que o que acontece é:

  • Leia a linha 1 para $0 com o primeiro getline
  • Leia a linha 2 para tmp com o segundo getline
  • Imprima tmp , em seguida, $0 , ou seja, imprima a linha 2 e, em seguida, a linha 1
  • Repita com o próximo par de linhas: imprima a linha 4 e, em seguida, a linha 3, etc.

Com um número ímpar de linhas, a última linha passa por getline $0 , então getline tmp falha, mas você não está verificando o status de retorno, o que apenas deixa tmp inalterado e você acaba imprimindo o próximo para a última linha novamente.

    
por 28.05.2017 / 03:24