Was sind NR und FNR und was bedeutet "NR == FNR"?

83

Ich lerne Dateivergleich mit awk.

Ich fand Syntax wie unten,

awk 'NR==FNR{a[$1];next}$1 in a{print $1}' file1 file2

Ich konnte nicht verstehen, welche Bedeutung NR==FNRdies hat. Wenn ich es FNR==NRdann versuche, bekomme ich auch die gleiche Ausgabe?

Was genau macht es?

Amit
quelle
20
Würden Sie , wenn überrascht sein a==bund b==adas gleiche Ergebnis erzeugt?
Ed Morton
5
Siehe Two-file Processingauf backreference.org/2010/02/10/idiomatic-awk
Etan Reisner

Antworten:

91

In awk FNRbezieht sich auf die Datensatznummer (normalerweise die Zeilennummer) in der aktuellen Datei und NRauf die gesamte Datensatznummer. Der Operator ==ist ein Vergleichsoperator, der true zurückgibt, wenn die beiden umgebenden Operanden gleich sind.

Dies bedeutet, dass die Bedingung NR==FNRnur für die erste Datei gilt, da FNRsie jedoch für die erste Zeile jeder Datei auf 1 zurückgesetzt wirdNR weiter zunimmt.

Dieses Muster wird normalerweise verwendet, um Aktionen nur für die erste Datei auszuführen. Das nextInnere des Blocks bedeutet, dass alle weiteren Befehle übersprungen werden, sodass sie nur für andere als die ersten Dateien ausgeführt werden.

Die Bedingung FNR==NRvergleicht dieselben zwei Operanden wie NR==FNR, verhält sich also genauso.

Tom Fenech
quelle
3
"=" wird manchmal verwendet, um die Gleichheit zu testen und manchmal um eine Zuweisung vorzunehmen. FNR == NR würde sich von NR == FNR unterscheiden, wenn das doppelte Gleichheitszeichen für die Zuweisung verwendet würde. Für jemanden, der mit awk nicht vertraut ist, wie diesen Fragesteller, erscheint es vernünftig zu fragen, ob er derselbe ist.
Todd Walton
@ToddWalton Guter Punkt! Ein weiteres Beispiel: a='3x'; if [[ $a == 3* ]]; then echo yes; fiund Sie können nicht beide Seiten von wechseln ==.
Walter A
@WalterA ja das stimmt (zumindest in Bash). Schlagen Sie eine Verbesserung meiner Antwort vor?
Tom Fenech
1
Nein, deine Antwort ist in Ordnung. Ich mag es wirklich zu sehen, dass die Community unsere Antworten genauso mag. Wir verwenden verschiedene Stile und beide werden als sehr hilfreich angesehen. Ich habe dir gerade eine Gegenstimme gegeben, also haben wir für diesen Moment die gleiche Anzahl von Gegenstimmen.
Walter A
70

Suchen Sie in Datei2 nach Schlüsseln (erstes Wort der Zeile), die sich auch in Datei1 befinden.
Schritt 1: Füllen Sie Array a mit den ersten Wörtern von Datei 1:

awk '{a[$1];}' file1

Schritt 2: Füllen Sie Array a und ignorieren Sie Datei 2 im selben Befehl. Überprüfen Sie dazu die Gesamtzahl der bisherigen Datensätze mit der Nummer der aktuellen Eingabedatei.

awk 'NR==FNR{a[$1]}' file1 file2

Schritt 3: Ignorieren Sie Aktionen, die }beim Parsen von Datei 1 auftreten können

awk 'NR==FNR{a[$1];next}' file1 file2 

Schritt 4: Drucken Sie den Schlüssel von Datei2, wenn er im Array gefunden wird. A.

awk 'NR==FNR{a[$1];next} $1 in a{print $1}' file1 file2
Walter A.
quelle
4
Genialer Abbau dieses Einzeilers. Ist das Semikolon in Schritt 1 erforderlich?
Tomasz Gandor
2
@TomaszGandor Das Semikolon wird in Schritt 1 nicht benötigt. Ich hätte es in Schritt 3 hinzufügen können, ist aber ;nexteine seltsame Ergänzung (möchte nextdas Semikolon in Schritt 3 hinzufügen und benötigen). Sie können Schritt 1 mit testen awk '{a[$1]} END { for (k in a) { print "a[k]=" k } }' file1.
Walter A
43

Nachschlagen NRund FNRin der awk Handbuch und dann fragen Sie sich , was die Bedingung , unter dem sich NR==FNRin dem folgenden Beispiel:

$ cat file1
a
b
c

$ cat file2
d
e

$ awk '{print FILENAME, NR, FNR, $0}' file1 file2
file1 1 1 a
file1 2 2 b
file1 3 3 c
file2 4 1 d
file2 5 2 e
Ed Morton
quelle
Ist es auch möglich, die Nummer der zu verarbeitenden Datei zu drucken? Gibt es dafür eine eingebaute Variable? (Ich weiß, wir könnten eine Variable dafür erstellen und sie jedes Mal
erhöhen,
In GNU awk ist diese Variable ARGIND, sonst können Sie tun FNR==1{ print ++file_nr }.
Ed Morton
Wenn ich darf, ist die Beantwortung einer Frage mit einer anderen Frage nicht so effizient;)
Florian Castelain
Ich habe keine Frage gestellt, ich habe gezeigt, wie man die Antwort auf die OP-Frage bekommt.
Ed Morton
20

Es gibt awk integrierte Variablen.

NR - Es gibt die Gesamtzahl der verarbeiteten Datensätze an.

FNR - Es gibt die Gesamtzahl der Datensätze für jede Eingabedatei an.

saß
quelle
14

Angenommen, Sie haben Dateien a.txt und b.txt mit

cat a.txt
a
b
c
d
1
3
5
cat b.txt
a
1
2
6
7

Denken Sie daran, dass NR und FNR awk integrierte Variablen sind. NR - Gibt die Gesamtzahl der verarbeiteten Datensätze an. (in diesem Fall sowohl in a.txt als auch in b.txt) FNR - Gibt die Gesamtzahl der Datensätze für jede Eingabedatei an (Datensätze in a.txt oder b.txt).

awk 'NR==FNR{a[$0];}{if($0 in a)print FILENAME " " NR " " FNR " " $0}' a.txt b.txt
a.txt 1 1 a
a.txt 2 2 b
a.txt 3 3 c
a.txt 4 4 d
a.txt 5 5 1
a.txt 6 6 3
a.txt 7 7 5
b.txt 8 1 a
b.txt 9 2 1

Lassen Sie "Weiter" hinzufügen, um die erste Übereinstimmung mit NR == FNR zu überspringen

in b.txt und in a.txt

awk 'NR==FNR{a[$0];next}{if($0 in a)print FILENAME " " NR " " FNR " " $0}' a.txt b.txt
b.txt 8 1 a
b.txt 9 2 1

in b.txt aber nicht in a.txt

 awk 'NR==FNR{a[$0];next}{if(!($0 in a))print FILENAME " " NR " " FNR " " $0}' a.txt b.txt
b.txt 10 3 2
b.txt 11 4 6
b.txt 12 5 7

awk 'NR==FNR{a[$0];next}!($0 in a)' a.txt b.txt
2
6
7
Don Kepler Brian Seremba
quelle