sort 與 uniq:資料排序與去重的實踐
在 Linux 系統管理與資料處理的日常工作中,我們經常需要面對雜亂無章的文字資料。無論是分析伺服器日誌、整理用戶清單,還是清洗 CSV 數據,排序(Sorting)與去重(Deduplication)都是最基礎且頻繁的操作。雖然現代有 Python、AWK 甚至 SQL 等強大的工具,但在快速檢查資料或進行管道(Pipeline)處理時,sort 與 uniq 這對黃金組合依然不可取代。
這篇文章將深入解析這兩個指令的核心用法,並透過實際範例,帶你掌握高效處理文字資料的技巧。
核心指令解析:sort
sort 指令負責將輸入的資料行進行排序。預設情況下,它會依據 ASCII 碼值進行升序排列。然而,透過各種選項,我們可以輕鬆應對數字、日期或特定欄位的複雜排序需求。
基礎排序與數字排序
當我們處理包含數字的資料時,預設的字串排序會導致錯誤的結果(例如 10 會排在 2 前面)。此時,必須使用 -n 選項強制進行數值排序。
# 建立測試檔案
echo -e "10\n2\n1\n20" > numbers.txt
# 預設字串排序(錯誤的結果)
sort numbers.txt
# 輸出:1, 10, 2, 20
# 正確的數值排序
sort -n numbers.txt
# 輸出:1, 2, 10, 20
多條件排序
實務上,我們常需要「先依條件 A 排序,若 A 相同則依條件 B 排序」。這可以透過 -k 選項指定欄位(Field)來達成。假設我們有一個學生成績清單,格式為 姓名 國文 英文:
echo -e "Alice 85 90\nBob 90 85\nAlice 90 85\nCharlie 85 90" > grades.txt
# 先依國文成績(第2欄)降序排列,若國文相同,再依英文成績(第3欄)降序排列
sort -t ' ' -k2,2nr -k3,3nr grades.txt
這裡 -t ' ' 指定空白鍵為欄位分隔符號,-k2,2nr 表示從第 2 欄到第 2 欄,以數值(n)且反向(r)排序。
核心指令解析:uniq
uniq 指令的作用非常單純:移除相鄰的重複行。注意關鍵在於「相鄰」。如果重複的資料被其他資料隔開,uniq 是無法將其合併的。因此,uniq 幾乎總是搭配 sort 使用。
基本去重與計數
echo -e "apple\nbanana\napple\ncherry\nbanana\napple" > fruits.txt
# 基本去重(需先排序)
sort fruits.txt | uniq
# 輸出:apple, banana, cherry
# 顯示每行出現的次數
sort fruits.txt | uniq -c
# 輸出:
# 3 apple
# 2 banana
# 1 cherry
-c 選項非常實用,它能讓我們快速掌握資料的分佈情況。
找出重複或唯一的行
除了統計次數,我們有時需要明確知道哪些是重複的,或哪些是唯一的。
# 僅顯示重複出現的行(會顯示所有重複項,包含第一次出現的)
sort fruits.txt | uniq -d
# 輸出:
# apple
# banana
# 僅顯示出現過一次的行
sort fruits.txt | uniq -u
# 輸出:
# cherry
實戰應用:日誌分析與資料清洗
讓我們結合 sort 與 uniq 來解決一個常見問題:找出伺服器日誌中出現頻率最高的 IP 地址。假設我們有一個名為 access.log 的檔案,其中包含多行 HTTP 請求記錄。
# 假設 IP 位於每行的開頭
# 192.168.1.1 GET /index.html
# 10.0.0.5 POST /api/data
# 192.168.1.1 GET /login
# 步驟 1: 提取 IP (使用 awk)
# 步驟 2: 排序
# 步驟 3: 去重並計數
# 步驟 4: 依計數數值降序排列,並顯示前 10 名
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10
這一行指令是 Linux 系統管理員的必備技能,它能瞬間揭示潛在的 DDoS 攻擊來源或最熱門的訪問端。
常見問題與注意事項
1. 為什麼 uniq 沒有去除重複資料?
這是新手最常遇到的陷阱。uniq 只會移除相鄰且完全相同的行。如果資料未經排序,重複的項目可能分散在檔案的不同位置。
解決方案: 務必在 uniq 前加上 sort。
# 錯誤示範
echo -e "B\nA\nB" | uniq
# 輸出:B, A, B (重複的 B 未被移除)
# 正確示範
echo -e "B\nA\nB" | sort | uniq
# 輸出:A, B
2. 排序結果與預期不符(大小寫問題)
預設的 sort 是基於 ASCII 碼排序,這意味著大寫字母會排在小寫字母之前(例如 Z 會排在 a 前面)。若希望進行不區分大小寫的字典序排序,可以使用 -f(fold)選項。
echo -e "apple\nBanana\ncherry" | sort
# 輸出:Banana, apple, cherry (B < a)
echo -e "apple\nBanana\ncherry" | sort -f
# 輸出:apple, Banana, cherry (忽略大小寫)
小結
sort 與 uniq 雖然看似簡單,但它們是 Linux 文字處理管道中不可或缺的基石。透過熟練運用 -n、-k、-c 等選項,並理解其底層邏輯(特別是 uniq 依賴相鄰性),你可以輕鬆處理從簡單的清單整理到複雜的日誌分析等多種任務。在 Ubuntu 22.04 與 Debian 12 等現代發行版中,這些指令的效能與相容性皆表現優異,值得每一位 Linux 使用者深入掌握。