Wie funktioniert „getline“ in AWK?

Wie funktioniert „getline“ in AWK?

Ich habe ein Beispiel mit der AWK-Funktion zusammengestellt getlineund es verwirrt mich.

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

Ich lese die nächste Zeile in eine Variable mit dem Namen, tmpdie sich nicht ändert $0, wie die ersten beiden Ausgabezeilen bestätigen:

bar
foo

Dies wird durch die folgende Tabelle bestätigt, die entnommen wurde ausDie Programmiersprache AWKauf Seite 62:

Bildbeschreibung hier eingeben

Ich weiß, dass die integrierten NRund FNRdie Anzahl der bisher gelesenen Zeilen darstellen. Ich denke, das ist der Schlüssel zum Verständnis dessen, was vor sich geht, aber ich bin verwirrt, wie sich Änderungen NRwährend eines Durchlaufs auf zukünftige Durchläufe auswirken.

Ich hatte erwartet, dass die nächsten beiden Zeilen lauten würden:

baz
bar

weil beim zweiten Durchgang $0 == barund tmp == baz.

Dann hatte ich erwartet, dass die nächsten beiden Zeilen tatsächlich nur aus einer Zeile bestehen würden:

baz

weil beim dritten Durchgang $0 == bazund tmp == null.

Meine erwartete Ausgabe ist also:

bar
foo
baz
bar
baz

Ich denke, dass das Verständnis der Änderungen NRwährend der Awk-Schleife der Schlüssel zum Verständnis dieser Ausgabe ist.

  • Können Sie erklären, warum meine erwartete Ausgabe falsch ist?Undwarum ist die tatsächliche Ausgabe richtig?

Ich laufe awk version 20070501weitermacOS 10.12.1

Antwort1

Ich denke, was Sie übersehen, ist, dass in der Einstellung NR, getlinein der Tatverbrauchtdie Zeile. Beim zweiten Aufruf barist also bereits verschwunden und $0ist baz; getlineder Versuch, eine andere Zeile zu lesen, schlägt fehl; und der Wert von tmpbleibt unverändert (d. h. gleich bar).

Es ist möglicherweise leichter zu verstehen, wenn Sie den Rückgabewert von überprüfen getline:

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

Antwort2

Es sollte klar werden, wenn Sie sozusagen das Gesamtbild betrachten. Ein awk-Programm ist eine Schleife um den Programmtext, die eine Zeile liest und dann das Programm in dieser Zeile ausführt. Wenn Sie eine Zeile innerhalb des Programms lesen, bekommt die umgebende Schleife diese Zeile nicht zu sehen: Sie wurde bereits verbraucht.

Zum Beispiel Ihr Programm

{ getline tmp; print tmp; print $0 }

könnte geschrieben werden als

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

Der BEGINBlock wird einmal zu Beginn des Programms ausgeführt, und das Programm tut hier nichts anderes – natürlich ist dies eine höchst unidiomatische Art, Awk-Code zu schreiben.

Hier sollte klar sein, dass Folgendes passiert:

  • Lesen Sie Zeile 1 bis $0mit dem erstengetline
  • Lesen Sie Zeile 2 tmpmit der zweitengetline
  • tmpDann drucken $0, d. h. erst Zeile 2 und dann Zeile 1 drucken
  • Wiederholen Sie dies mit dem nächsten Zeilenpaar: Drucken Sie Zeile 4, dann Zeile 3 usw.

Bei einer ungeraden Zeilenanzahl wird die letzte Zeile durchlaufen getline $0, schlägt dann getline tmpaber fehl, da Sie den Rückgabestatus nicht prüfen tmp, bleibt dieser einfach unverändert und Sie drucken am Ende die vorletzte Zeile erneut.

verwandte Informationen