Ich habe ein Beispiel mit der AWK-Funktion zusammengestellt getline
und 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, tmp
die 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:
Ich weiß, dass die integrierten NR
und FNR
die 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 NR
wä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 == bar
und 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 == baz
und tmp == null
.
Meine erwartete Ausgabe ist also:
bar
foo
baz
bar
baz
Ich denke, dass das Verständnis der Änderungen NR
wä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 20070501
weitermacOS 10.12.1
Antwort1
Ich denke, was Sie übersehen, ist, dass in der Einstellung NR
, getline
in der Tatverbrauchtdie Zeile. Beim zweiten Aufruf bar
ist also bereits verschwunden und $0
ist baz
; getline
der Versuch, eine andere Zeile zu lesen, schlägt fehl; und der Wert von tmp
bleibt 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 BEGIN
Block 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
$0
mit dem erstengetline
- Lesen Sie Zeile 2
tmp
mit der zweitengetline
tmp
Dann 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 tmp
aber fehl, da Sie den Rückgabestatus nicht prüfen tmp
, bleibt dieser einfach unverändert und Sie drucken am Ende die vorletzte Zeile erneut.