
使用 awk 刪除重複項非常常見且簡單。但當我們只比較一列時,我只需要列印那些重複的行。我嘗試了這個命令:
awk 'seen[$2]++'
但正如你所看到的,它有缺陷。它會列印重複項,但僅限於它們第二次出現後。我才剛開始習慣 unix 和 bash,所以如果你能向我解釋解決方案那就太好了。
答案1
我可以看到有兩種方法可以做到這一點:
迭代文件兩次:
在第一次迭代中,計算每個 $2 出現的次數。
在第二次迭代中,僅列印計數大於 1 的行awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
資料的單次迭代:
你需要統計每個$2出現的次數,和記住每 2 美元發生了哪些行。
這個答案使用 GNU awk 來表示陣列的陣列。輸出的順序不可能與輸入資料相同。它還必須將整個文件儲存在記憶體中。
gawk ' { lines[$2][++count[$2]] = $0 } END { for (x in lines) if (count[x] > 1) for (i=1; i<=count[x]; i++) print lines[x][i] } ' file
使用輸入檔進行測試:
$ cat file
a b
b b
c b
a c
a d
b d
a e
和預期產出
a b
b b
c b
a d
b d
答案2
使用相同的樣本輸入格倫傑克曼的回答
$ awk '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' file
a b
b b
c b
a d
b d
!seen[$2]++
如果$2
之前沒有遇過:fl[$2]=$0
保存第一行,我假設輸入未排序並且重複項可能出現在文件中的任何位置,因此基於$2
而不是僅臨時變數保存它c[$2]=1
類似地,將計數變數初始化為 1
$2 in seen
如果$2
之前發生過:if(c[$2]--){print fl[$2]}
首先列印上一行,計數器遞減,以便後續匹配的條件將失敗print
然後列印當前行
與一些其他輸入
$ cat ip.txt
6.2 : 897 : bar
3.1 : 32 : foo
1.2 : 123 : xyz
2.3 : 32 : baz
7.5 : 897 : boo
$ awk -F: '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' ip.txt
3.1 : 32 : foo
2.3 : 32 : baz
6.2 : 897 : bar
7.5 : 897 : boo
請注意,順序取決於重複發生的方式
答案3
當您迭代同一個檔案兩次時,您可以使用行號作為方便的索引;它可以使邏輯更清晰。
awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file
我在回答這個問題時使用了類似的技巧:
這個技巧的基礎是 Awk 將簡單地透過引用來創建一個變量,並且該index in arrayname
構造根據是否已使用指定索引創建了數組元素來返回 true 或 false。