Как работает `getline` в AWK?

Как работает `getline` в AWK?

Я составил пример с использованием getlineфункции AWK, и он меня сбивает с толку.

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

Я считываю следующую строку в переменную с именем, tmpкоторая не изменяется $0, что подтверждается первыми двумя строками вывода:

bar
foo

Это подтверждается следующей таблицей, взятой изЯзык программирования AWKна странице 62:

введите описание изображения здесь

Я знаю, что NRвстроенные и FNRпредставляют количество прочитанных строк. Я думаю, что это ключ к пониманию того, что происходит, но я не понимаю, как изменение NRв проходе влияет на будущие проходы.

Я ожидал, что следующие две строки будут такими:

baz
bar

потому что на втором проходе $0 == barи tmp == baz.

Тогда я ожидал, что следующие две строки на самом деле будут одной строкой:

baz

потому что на третьем проходе $0 == bazи tmp == null.

Итак, мой ожидаемый результат:

bar
foo
baz
bar
baz

Я думаю, что понимание того, как происходит изменение NRв цикле awk, является ключом к пониманию этого вывода.

  • Можете ли вы объяснить, почему мой ожидаемый результат неверен?ипочему фактический результат правильный?

Я бегу awk version 20070501дальшеmacOS 10.12.1

решение1

Я думаю, что вы упускаете из виду то, что в установке NR, getlineпо сути ,потребляетстрока. Так что при втором вызове barуже нет и $0есть baz; getlineпытается прочитать другую строку и терпит неудачу; и значение tmpостается неизменным (т.е. равным bar).

Возможно, это будет легче понять, если вы проверите возвращаемое значение getline:

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

решение2

Это должно стать ясно, если вы посмотрите на общую картину, так сказать. Программа awk — это цикл вокруг текста программы, который считывает одну строку, а затем выполняет программу на этой строке. Если вы считываете строку внутри программы, то окружающий цикл не видит эту строку: она уже была использована.

Например, ваша программа

{ getline tmp; print tmp; print $0 }

можно записать как

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

Блок BEGINвыполняется один раз в начале программы, и здесь программа больше ничего не делает — конечно, это крайне неидиоматический способ написания кода awk.

Здесь должно быть ясно, что происходит следующее:

  • Прочитайте строку 1 вместе $0с первойgetline
  • Прочитайте строку 2 tmpсо вторымgetline
  • tmpЗатем распечатать $0, т.е. распечатать строку 2, а затем строку 1.
  • Повторите со следующей парой строк: выведите строку 4, затем строку 3 и т. д.

При нечетном количестве строк последняя строка проходит через getline $0, затем getline tmpпроисходит сбой, но вы не проверяете статус возврата, поэтому он просто остается tmpнеизменным, и в итоге вы снова печатаете предпоследнюю строку.

Связанный контент