comm 比較排序檔案的差異
在 Linux 系統管理與腳本編寫的領域中,檔案內容的比對與合併是極其常見的需求。雖然 diff 指令以其強大的差異報告功能聞名,但在某些特定場景下,我們需要的不是逐行的詳細差異描述,而是純粹的「集合運算」——例如找出檔案 A 有但檔案 B 沒有的行,或是兩份名單的交集。這時候,comm 指令便是解決這類問題的神器。
comm(compare)指令專門用於比較兩個已排序的檔案,並以列為單位顯示它們的差異。它不像 diff 那樣複雜,卻能以更精簡的方式輸出結果,非常適合用於資料清洗、名單比對或自動化腳本中。
comm 的基本運作邏輯
在深入指令之前,必須先理解 comm 的核心原則:輸入檔案必須是排序過的。如果檔案未排序,comm 的結果將是不可靠且無意義的。此外,comm 的輸出預設分為三個欄位:
- 第一欄:僅出現在檔案 1 中的行。
- 第二欄:僅出現在檔案 2 中的行。
- 第三欄:同時出現在檔案 1 和檔案 2 中的行(交集)。
實戰範例:建立測試環境
為了讓讀者能清楚理解,我們先在 Ubuntu 22.04 或 Debian 12 上建立兩個簡單的測試檔案。
# 建立檔案 list_a.txt
cat <<EOF > list_a.txt
apple
banana
cherry
date
elderberry
fig
EOF
# 建立檔案 list_b.txt
cat <<EOF > list_b.txt
banana
cherry
grape
kiwi
EOF
請注意,上述內容已經預設是依字母順序排列的。如果來源資料未排序,請務必先使用 sort 指令處理:sort list_a.txt > list_a_sorted.txt。
基本使用與輸出解析
執行最基本的 comm 指令:
comm list_a.txt list_b.txt
輸出結果如下:
apple
banana
cherry
date
elderberry
fig
grape
kiwi
觀察輸出格式,你會發現每行開頭可能有空白或製表符號(Tab)。這是 comm 用來區分欄位的方式:
- 沒有前導空白:代表該行僅存在於 list_a.txt(第一欄)。
- 一個製表符號前導:代表該行僅存在於 list_b.txt(第二欄)。
- 兩個製表符號前導:代表該行同時存在於兩份檔案中(第三欄)。
使用選項過濾輸出
在實際應用中,我們通常不需要看到所有三個欄位。comm 提供了 -1、-2、-3 選項來隱藏對應的欄位。
1. 找出 list_a 有但 list_b 沒有的項目
若要獲取「差集」(A - B),我們需要隱藏第二欄(僅在 B 中的項目)和第三欄(交集)。
comm -23 list_a.txt list_b.txt
輸出:
apple
date
elderberry
fig
這正是我們想要的結果:apple, date, elderberry, fig 是 list_a 獨有的。
2. 找出 list_b 有但 list_a 沒有的項目
若要獲取「B - A」,則隱藏第一欄和第三欄。
comm -13 list_a.txt list_b.txt
輸出:
grape
kiwi
3. 找出兩者的交集
若要獲取「交集」(A ∩ B),我們只需隱藏第一欄和第二欄,只顯示第三欄。
comm -12 list_a.txt list_b.txt
輸出:
banana
cherry
4. 僅顯示差異行(不含交集)
有時我們只關心「哪裡不同」,而不關心誰多誰少。可以使用 -23 來隱藏第二欄和第三欄,但這會只剩下第一欄。若要看兩邊獨有的部分,可以分別執行 -23 和 -13,或者使用更聰明的技巧:
# 顯示僅在 A 中的項目
comm -23 list_a.txt list_b.txt
# 顯示僅在 B 中的項目
comm -13 list_a.txt list_b.txt
常見問題與注意事項
1. 為什麼輸出結果是空的或錯誤的?
這是新手最常遇到的問題。comm 對排序非常敏感。如果 list_a.txt 中的 Apple 和 apple 混用,或者檔案不是依 ASCII 碼排序,comm 會認為它們是不同的字串,導致結果混亂。
解決方案:在比較前,務必確認檔案已排序。若檔案內容包含大小寫混合,建議使用 sort -f(忽略大小寫)來排序,但需注意這樣會改變比較邏輯。
sort -f list_a.txt > list_a_sorted.txt
sort -f list_b.txt > list_b_sorted.txt
comm list_a_sorted.txt list_b_sorted.txt
2. 如何處理大檔案或效能問題?
comm 是記憶體友好型的指令,它採用類似合併排序(Merge Sort)的演算法,只需一次掃描兩個檔案即可產生結果,時間複雜度為 O(N+M),效能遠優於使用 grep -F -x -v 等指令進行逐行比對。
然而,若檔案極大(例如 GB 級別),請確保磁碟 I/O 足夠快,並避免在排序階段產生過多的暫存檔。若來源資料未排序,sort 步驟可能會成為效能瓶頸,此時可考慮使用 sort -T /dev/shm 將暫存檔放在記憶體磁碟機上以提升速度。
小結
comm 是一個被低估的 Linux 工具,特別適合用於處理已排序的純文字列表。它簡潔的輸出格式和強大的過濾選項,使其成為 Shell 腳本中進行集合運算的理想選擇。記住兩個關鍵點:輸入必須排序,以及善用 -1, -2, -3 選項來精準控制輸出內容。掌握 comm,能讓你在處理名單比對、資料稽核等任務時更加得心應手。