NR và FNR là gì và “NR == FNR” ngụ ý gì?


83

Tôi đang học so sánh tệp bằng cách sử dụng awk.

Tôi đã tìm thấy cú pháp như dưới đây,

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

Tôi không thể hiểu ý nghĩa của NR==FNRviệc này là gì? Nếu tôi thử với FNR==NRthì tôi cũng nhận được kết quả tương tự?

Chính xác thì nó làm gì?


20
Bạn có ngạc nhiên nếu a==bvà cho b==ara kết quả tương tự?
Ed Morton

Câu trả lời:


91

Trong awk, FNRđề cập đến số bản ghi (thường là số dòng) trong tệp hiện tại và NRđề cập đến tổng số bản ghi. Toán tử ==là một toán tử so sánh, trả về true khi hai toán hạng xung quanh bằng nhau.

Điều này có nghĩa là điều kiện NR==FNRchỉ đúng với tệp đầu tiên, vì được FNRđặt lại về 1 cho dòng đầu tiên của mỗi tệp nhưng NRvẫn tiếp tục tăng.

Mẫu này thường được sử dụng để thực hiện các hành động chỉ trên tệp đầu tiên. Bên nexttrong khối có nghĩa là bất kỳ lệnh nào khác đều bị bỏ qua, vì vậy chúng chỉ được chạy trên các tệp khác với tệp đầu tiên.

Điều kiện FNR==NRso sánh hai toán hạng giống nhau NR==FNR, vì vậy nó hoạt động theo cùng một cách.


3
"=" đôi khi được sử dụng để kiểm tra tính bình đẳng và đôi khi để thực hiện một phép gán. FNR == NR sẽ khác với NR == FNR nếu dấu bằng kép đang được sử dụng để gán. Vì vậy, đối với một người không quen thuộc với awk, chẳng hạn như người hỏi này, có vẻ hợp lý khi hỏi họ có giống nhau không.
Todd Walton

@ToddWalton Điểm tốt! Một ví dụ khác: a='3x'; if [[ $a == 3* ]]; then echo yes; fivà bạn không thể chuyển đổi cả hai bên ==.
Walter A

@WalterA vâng, điều đó đúng (ít nhất là trong Bash). Bạn có đề xuất bất kỳ cải thiện nào cho câu trả lời của tôi không?
Tom Fenech

1
Không, câu trả lời của bạn là ổn. Tôi thực sự thích thấy rằng cộng đồng cũng thích câu trả lời của chúng tôi. Chúng tôi sử dụng các phong cách khác nhau và cả hai đều rất hữu ích. Tôi vừa đưa cho bạn một phiếu ủng hộ, vì vậy cho đến thời điểm này chúng ta có cùng một số phiếu ủng hộ.
Walter A

70

Tìm các khóa (từ đầu tiên của dòng) trong tệp2 cũng có trong tệp1.
Bước 1: Điền vào mảng a với các từ đầu tiên của tệp 1:

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

Bước 2: Điền vào mảng a và bỏ qua tệp 2 trong cùng một lệnh. Đối với điều này, hãy kiểm tra tổng số bản ghi cho đến thời điểm hiện tại với số lượng tệp đầu vào hiện tại.

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

Bước 3: Bỏ qua các hành động có thể xảy ra sau }khi phân tích cú pháp tệp 1

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

Bước 4: Print key của file2 khi được tìm thấy trong mảng a

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

4
Lần gỡ xuống tuyệt vời của một lớp lót này. Dấu chấm phẩy ở Bước 1 có cần thiết không?
Tomasz Gandor

2
@TomaszGandor Dấu chấm phẩy không cần thiết ở bước 1. Tôi có thể đã thêm nó ở bước 3, nhưng ;nextlà một phép bổ sung kỳ lạ (giống như thêm nextvà cần dấu chấm phẩy ở bước 3). Bạn có thể kiểm tra bước 1 với awk '{a[$1]} END { for (k in a) { print "a[k]=" k } }' file1.
Walter A

43

Tra cứu NRFNRtrong cuốn hướng dẫn awk và sau đó hãy tự hỏi điều kiện là gì, theo đó NR==FNRtrong ví dụ sau:

$ 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

có thể in số lượng tệp đang được xử lý không? có một biến tích hợp cho điều đó không? (Tôi biết chúng ta có thể tạo một biến cho điều đó và tăng nó mỗi khi NR là một)
LEo 19/09 '19

Trong GNU awk, biến đó là ARGIND, nếu không bạn có thể làm FNR==1{ print ++file_nr }.
Ed Morton

Nếu tôi có thể, trả lời một câu hỏi bằng một câu hỏi khác không hiệu quả lắm;)
Florian Castelain

Tôi không đặt câu hỏi, tôi đã chỉ cách nhận câu trả lời cho câu hỏi OP.
Ed Morton

20

awkcác biến tích hợp sẵn.

NR - Nó cho biết tổng số bản ghi được xử lý.

FNR - Nó cho biết tổng số bản ghi cho mỗi tệp đầu vào.


14

Giả sử bạn có Tệp a.txt và b.txt với

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

Hãy nhớ NR và FNR là các biến tích hợp sẵn của awk. NR - Cung cấp tổng số bản ghi được xử lý. (trong trường hợp này là cả a.txt và b.txt) FNR - Cung cấp tổng số bản ghi cho mỗi tệp đầu vào (bản ghi ở dạng a.txt hoặc 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

cho phép Thêm "tiếp theo" để bỏ qua đối sánh đầu tiên với NR == FNR

trong b.txt và trong 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

trong b.txt nhưng không phải trong 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.