Co to są NR i FNR i co oznacza „NR == FNR”?


85

Uczę się porównywania plików za pomocą awk.

Znalazłem składnię jak poniżej,

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

Nie mogłem zrozumieć, jakie to ma znaczenie NR==FNR? Jeśli spróbuję z, FNR==NRto również otrzymam ten sam wynik?

Co to właściwie robi?


20
Byłbyś zaskoczony, jeśli a==bi b==awyprodukował sam wynik?
Ed Morton,

Odpowiedzi:


93

W awk FNRodnosi się do numeru rekordu (zazwyczaj numeru wiersza) w bieżącym pliku i NRodnosi się do całkowitej liczby rekordów. Operator ==jest operatorem porównania, który zwraca prawdę, gdy dwa otaczające operandy są równe.

Oznacza to, że warunek NR==FNRjest prawdziwy tylko dla pierwszego pliku, ponieważ FNRresetuje się z powrotem do 1 dla pierwszego wiersza każdego pliku, ale NRwciąż rośnie.

Ten wzorzec jest zwykle używany do wykonywania działań tylko na pierwszym pliku. nextWewnątrz środków blokowych jakiekolwiek kolejne polecenia są pomijane, więc są one uruchamiane tylko na plikach innych niż pierwszy.

Warunek FNR==NRporównuje te same dwa operandy co NR==FNR, więc zachowuje się w ten sam sposób.


3
„=” jest czasami używane do testowania równości, a czasami do przypisywania. FNR == NR byłby inny niż NR == FNR, gdyby do przypisania użyto podwójnego znaku równości. Więc dla kogoś, kto nie jest zaznajomiony z awk, takim jak ten asker, rozsądne wydaje się zapytanie, czy to to samo.
Todd Walton,

@ToddWalton Słuszna uwaga! Inny przykład: a='3x'; if [[ $a == 3* ]]; then echo yes; fii nie można przełączać obu stron ==.
Walter A

@Walter Tak, to prawda (przynajmniej w Bash). Czy sugerujecie jakieś ulepszenie mojej odpowiedzi?
Tom Fenech,

1
Nie, twoja odpowiedź jest w porządku. Bardzo lubię widzieć, że społeczność tak samo lubi nasze odpowiedzi. Używamy różnych stylów i oba są uważane za bardzo pomocne. Właśnie dałem Ci głos za, więc w tej chwili mamy taką samą liczbę głosów za.
Walter A

72

Poszukaj kluczy (pierwsze słowo wiersza) w pliku2, które również znajdują się w pliku1.
Krok 1: Wypełnij tablicę a pierwszymi słowami z pliku 1:

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

Krok 2: Wypełnij tablicę a i zignoruj ​​plik 2 w tym samym poleceniu. W tym celu sprawdź łączną liczbę rekordów do tej pory z numerem bieżącego pliku wejściowego.

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

Krok 3: Zignoruj ​​akcje, które mogą nastąpić po }podczas analizowania pliku 1

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

Krok 4: wydrukuj klucz pliku2, gdy zostanie znaleziony w tablicy a

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

4
Genialne obalenie tej jednej linijki. Czy średnik w kroku 1 jest konieczny?
Tomasz Gandor

2
@TomaszGandor Średnik nie jest potrzebny w kroku 1. Mogłem go dodać w kroku 3, ale ;nextjest to dziwny dodatek (lubię dodać nexti potrzebuję średnika w kroku 3). Możesz przetestować krok 1 za pomocą awk '{a[$1]} END { for (k in a) { print "a[k]=" k } }' file1.
Walter A

45

Zajrzyj w górę NRiw FNRpodręczniku awk, a następnie zadaj sobie pytanie, jaki jest warunek NR==FNRw poniższym przykładzie:

$ 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

czy można również wydrukować numer przetwarzanego pliku? czy jest do tego wbudowana zmienna? (Wiem, że moglibyśmy stworzyć zmienną do tego i zwiększać ją za każdym razem, gdy NR wynosi jeden)
LEo

W GNU awk tą zmienną jest ARGIND, w przeciwnym razie możesz to zrobić FNR==1{ print ++file_nr }.
Ed Morton

Jeśli mogę, odpowiedź na pytanie innym pytaniem nie jest tak skuteczna;)
Florian Castelain

Nie zadałem pytania, pokazałem, jak uzyskać odpowiedź na pytanie PO.
Ed Morton,

20

Istnieją awkzmienne wbudowane.

NR - Podaje całkowitą liczbę przetworzonych rekordów.

FNR - Podaje całkowitą liczbę rekordów dla każdego pliku wejściowego.


15

Zakładając, że masz pliki a.txt i b.txt z rozszerzeniem

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

Należy pamiętać, że NR i FNR to zmienne wbudowane w awk. NR - podaje całkowitą liczbę przetworzonych rekordów. (w tym przypadku zarówno w a.txt, jak i b.txt) FNR - podaje całkowitą liczbę rekordów dla każdego pliku wejściowego (rekordy w formacie a.txt lub 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

pozwala dodać „następny”, aby pominąć pierwszy dopasowany z NR == FNR

w b.txt i 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

w b.txt, ale nie w 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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.