[AWK] NR==FNR 的意思
[AWK] NR==FNR 的意思
簡介
很多時候,我們都會看到 awk 指令中含有 NR==FNR 這個短句,其實這昰一個 awk 中的常用語法。
NR 和 FNR 是兩個 awk 中的內置變量,NR 即是 total Number of input Records seen so far(到目前為止看到的總輸入記錄數),而 FNR 則是 Number of input Record in the current input File(當前文件中,到目前為止看到的輸入記錄數),輸入記錄通常是指一行,所以輸入記錄數通常是 line number(行號碼),當我們只處理一個檔案時,我們通常不會理會 NR,因為當我們處理第一個檔案時, NR 必定會等於 FNR。
| 內置變量 | 意思 |
|---|---|
| NR | The total Number of input Records seen so far. 到目前為止看到的總輸入記錄數 |
| FNR | The Number of input Record in the current input File seen so far. 當前文件中,到目前為止看到的輸入記錄數 |
查看 ‘NR’ 和 ‘FNR’
在以下例子中,我們可以直接看到 NR 和 FNR,好讓我們更清楚它們是甚麼。
- 首先,我們要創建兩個檔案。
noob@learnfromnoobs:~$ cat file1.txt
file1-line1
file1-line2
file1-line3
noob@learnfromnoobs:~$ cat file2.txt
file2-line1
file2-line2
file2-line3
- 我們可以利用以下指令顯示出我們剛建立的檔案的檔案名、
NR、FNR以及當前的行。
noob@learnfromnoobs:~$ awk '{print FILENAME, NR, FNR, $0}' file1.txt file2.txt
file1.txt 1 1 file1-line1
file1.txt 2 2 file1-line2
file1.txt 3 3 file1-line3
file2.txt 4 1 file2-line1
file2.txt 5 2 file2-line2
file2.txt 6 3 file2-line3
由以上輸出中,我們可以看到 NR 和 FNR 兩者都會隨著 awk 處理新一行而增加,唯一不同的是當 awk 處理另一個檔案時,FNR 會重置為 1 。
因此,NR==FNR 這個短句其實就在檢查我們是否正在處理引數(argument)中的第一個檔案。
常見實用例子
當我們需要處理兩個或以上的檔案時,NR==FNR 就會變很非常有用,以下是兩個常見的實用例子。(注意,其實還有其他方法可以實現跟以下例子相同的目標,我們只是討論在這些情況下可以如何利用 awk,你應該根據自己的情況選擇合適的工具。)
在進入討論前,讓我們先修改一下 file1.txt 和 file2.txt 旳內容。
noob@learnfromnoobs:~$ cat file1.txt
John
Tom
Tony
Alex
Michael
Kalvin
noob@learnfromnoobs:~$ cat file2.txt
Tony
Alex
Chris
1. 顯示兩個文件共有的行
我們可以利用 awk 顯示出兩個文件共有的行。
noob@learnfromnoobs:~$ awk 'NR==FNR { array[$0]; next } $0 in array' file1.txt file2.txt
Tony
Alex
這指令把第一個檔案中的所有行儲存在一個陣列(array)中,如果 file2.txt 的行跟陣列中的容內重覆,我們就會直接把它顯示出來(這是 awk 的默認操作),如果你仍然感到難以理解,我們可以把指令分拆成幾個部分:
NR==FNR { array[$0]; next }的意思是,如果當前的行是來自第一個檔案(file1.txt)的,就把它儲存在一個陣列中,否則,則直接跳過。$0 in array會查看file2.txt的行是否存在於我們剛建立的陣列(file1.txt的行)中,如果是這樣的話,我們就會直接把該行顯示出來。
2. 顯示文件1 中的所有行,但不包括那些同時存在於文件2、文件3 的行
在這篇文章中,我們已經討論了如何利用 awk 在兩個文件中,只顯示文件1 特有的行,現在,讓我們擴展已有的知識,使我們可以過濾更多文件中的行。
讓我們創建 file3.txt 並執行我們的指令。
noob@learnfromnoobs:~$ cat file3.txt
Tony
John
noob@learnfromnoobs:~$ awk 'NR==FNR { array[$0]; next } { delete array[$0] } END { for (key in array) { print key } }' file1.txt file2.txt file3.txt
Tom
Michael
Kalvin
我們可以把指令分拆成幾個部分:
NR==FNR { array[$0]; next }的意思是,如果當前的行是來自第一個檔案(file1.txt)的,就把它儲存在一個陣列中,否則,則直接跳過。- 當我們處理
file1.txt以外文件中的行時,awk 就會執行{ delete array[$0] },若該行已存在於陣列當中,awk 就會將該行從陣列中刪除。 - 當 awk 已經完成閱讀所有的輸入記錄, awk 就會執行
END { for (key in array) { print key } }把所有仍然在陣列中的 key (鍵)顯示出來,亦即文件1 特有的行。
我們也可以重新命名我們的檔案,令我們的指令更容易理解。
noob@learnfromnoobs:~$ cp file1.txt all.txt
noob@learnfromnoobs:~$ cp file2.txt remove1.txt
noob@learnfromnoobs:~$ cp file3.txt remove2.txt
noob@learnfromnoobs:~$ awk 'NR==FNR { array[$0]; next } { delete array[$0] } END{for (key in array) { print key } }' all.txt remove*.txt
Tom
Michael
Kalvin
把檔案重新命名後,我們更清楚知道這指令會顯示 all.txt 中的所有行,但不包括那些同時存在於 remove*.txt 的行。
總結
在這篇文章中,我們討論了:
NR和FNR是甚麼NR==FNR的意思是甚麼NR==FNR的常見實用例子
我希望你喜歡這篇文章,並從中得到了新知識。
記得要不斷學習,have fun!