Como funciona o `getline` no AWK?

Como funciona o `getline` no AWK?

Elaborei um exemplo usando a getlinefunção AWK e isso 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 tmpque não muda $0conforme confirmado pelas duas primeiras linhas de saída:

bar
foo

Isto é confirmado pela seguinte tabela retirada deA linguagem de programação AWKna página 62:

insira a descrição da imagem aqui

Eu sei que os NRe FNRembutidos representam o número de linhas lidas até agora. Acho que essa é a chave para entender o que está acontecendo, mas estou confuso sobre como a mudança NRdurante uma passagem afeta passagens futuras.

Eu esperava que as próximas duas linhas fossem:

baz
bar

porque na segunda passagem $0 == bare tmp == baz.

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

baz

porque na terceira passagem $0 == baze tmp == null.

Portanto, minha saída esperada é:

bar
foo
baz
bar
baz

Acho que entender como mudar NRdurante o loop awk é a chave para entender essa saída.

  • Você pode explicar por que minha saída esperada está erradaepor que a saída real está certa?

Estou awk version 20070501correndomacOS 10.12.1

Responder1

Acho que o que está faltando é que, na configuração NR, getlinena verdadeconsomea linha. Então, na segunda invocação, barjá desapareceu e $0é baz; getlinetenta ler outra linha e falha; e o valor de tmppermanece inalterado (ou seja, igual a bar).

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

awk '{ if ((getline tmp) > 0) print tmp; print $0 }' in
bar
foo
baz

Responder2

Deve ficar claro se você olhar o quadro geral, por assim dizer. Um programa awk é um loop em torno do texto do programa, que lê uma linha e depois executa o programa nesta linha. Se você ler uma linha dentro do programa, o loop ao redor não conseguirá ver esta 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 BEGINbloco é executado uma vez no início do programa, e aqui o programa não faz mais nada — é claro que esta é uma forma altamente não-idiomática de escrever código awk.

Aqui deve ficar claro que o que acontece é:

  • Leia a linha 1 até $0a primeiragetline
  • Leia a linha 2 tmpcom a segundagetline
  • Imprima tmpentão $0, ou seja, imprima a linha 2 e depois a linha 1
  • Repita com o próximo par de linhas: imprima a linha 4, depois a linha 3, etc.

Com um número ímpar de linhas, a última linha passa getline $0e getline tmpfalha, mas você não está verificando o status de retorno tmp, então isso simplesmente permanece inalterado e você acaba imprimindo a penúltima linha novamente.

informação relacionada