從基礎(chǔ)到高階詳解Linux?Shell文件去重的完整指南
在日常開發(fā)和運(yùn)維工作中,我們經(jīng)常會(huì)遇到文件內(nèi)容重復(fù)的場(chǎng)景——比如日志合并后存在大量重復(fù)條目、數(shù)據(jù)采集時(shí)出現(xiàn)冗余記錄、文本處理后殘留重復(fù)行等。手動(dòng)去重效率低下且容易出錯(cuò),而 Linux Shell 提供了多種高效的去重工具和技巧,能夠輕松應(yīng)對(duì)不同場(chǎng)景下的文件去重需求。
本文將從「基礎(chǔ)去重」到「高階實(shí)戰(zhàn)」,系統(tǒng)講解 Linux Shell 中文件去重的核心方法,包括 sort+uniq 組合、awk 高級(jí)去重、sed 精準(zhǔn)去重等,同時(shí)深入原理分析和性能對(duì)比,幫助你根據(jù)實(shí)際場(chǎng)景選擇最優(yōu)方案。
一、基礎(chǔ)去重:sort + uniq 組合(最常用)
1.1 核心原理
uniq 是 Linux 內(nèi)置的去重命令,但其僅能去除相鄰的重復(fù)行(底層原理:逐行讀取文件,與上一行內(nèi)容對(duì)比,相同則跳過,不同則輸出并更新“上一行緩存”)。因此,直接使用 uniq 無法處理非相鄰重復(fù)行,必須先通過 sort 命令將文件內(nèi)容排序,使重復(fù)行相鄰,再用 uniq 去重。
1.2 基本用法
場(chǎng)景 1:簡(jiǎn)單去重(保留首次出現(xiàn)的行)
# 語(yǔ)法:sort 文件名 | uniq sort data.txt | uniq
示例:data.txt 原始內(nèi)容
apple banana apple orange banana
執(zhí)行結(jié)果(去重后并排序)
apple banana orange
場(chǎng)景 2:統(tǒng)計(jì)重復(fù)次數(shù)(去重并顯示計(jì)數(shù))
sort data.txt | uniq -c
輸出結(jié)果(前綴為重復(fù)次數(shù))
2 apple
2 banana
1 orange場(chǎng)景 3:只保留重復(fù)行(篩選重復(fù)數(shù)據(jù))
sort data.txt | uniq -d
輸出結(jié)果(僅顯示出現(xiàn)次數(shù) ≥2 的行)
apple banana
場(chǎng)景 4:只保留不重復(fù)行(篩選唯一數(shù)據(jù))
sort data.txt | uniq -u
輸出結(jié)果(僅顯示出現(xiàn)次數(shù) =1 的行)
orange
1.3 關(guān)鍵參數(shù)詳解
| 參數(shù) | 作用 | 示例 |
|---|---|---|
-c | 顯示每行的重復(fù)次數(shù) | `sort data.txt |
-d | 僅顯示重復(fù)出現(xiàn)的行(次數(shù) ≥2) | `sort data.txt |
-u | 僅顯示不重復(fù)的行(次數(shù) =1) | `sort data.txt |
-i | 忽略大小寫差異(如 Apple 和 apple 視為重復(fù)) | `sort data.txt |
-f N | 忽略前 N 個(gè)字段的差異(字段以空格分隔) | `sort data.txt |
-s N | 忽略前 N 個(gè)字符的差異 | `sort data.txt |
1.4 注意事項(xiàng)
- 必須先排序:
uniq僅對(duì)比相鄰行,未排序的文件直接使用uniq會(huì)導(dǎo)致去重不徹底(如上述示例中直接uniq data.txt無法去除非相鄰的apple和banana)。 - 空白行處理:
uniq會(huì)將空白行視為相同內(nèi)容,如需保留空白行可結(jié)合grep過濾:sort data.txt | grep -v '^$' | uniq(先去除空白行再去重)。 - 性能考量:
sort命令的時(shí)間復(fù)雜度為 O(n log n),對(duì)于超大文件(GB 級(jí)),需結(jié)合后續(xù)高階方法優(yōu)化。
二、高階去重:awk 命令(靈活處理復(fù)雜場(chǎng)景)
sort+uniq 適合簡(jiǎn)單的全行去重,但在實(shí)際工作中,我們常遇到「部分字段去重」「保留最后一次出現(xiàn)的行」「按條件去重」等復(fù)雜需求,此時(shí) awk 命令的靈活性優(yōu)勢(shì)凸顯。
2.1 核心原理
awk 基于「關(guān)聯(lián)數(shù)組」(鍵值對(duì))實(shí)現(xiàn)去重:將需要去重的字段作為數(shù)組的鍵,遍歷文件時(shí)判斷鍵是否存在——存在則跳過(或覆蓋),不存在則存儲(chǔ)并輸出。無需提前排序,支持自定義去重邏輯。
2.2 典型場(chǎng)景實(shí)戰(zhàn)
場(chǎng)景 1:部分字段去重(保留首次出現(xiàn)的行)
假設(shè) user.txt 內(nèi)容如下(第一列為用戶 ID,第二列為用戶名),需按用戶 ID 去重(相同 ID 僅保留首次出現(xiàn)的行):
101 Alice 102 Bob 101 Charlie 103 Dave 102 Eve
命令:
awk '!a[$1]++' user.txt
輸出結(jié)果(按 ID 去重,保留首次出現(xiàn)的行):
101 Alice 102 Bob 103 Dave
原理解析:
$1表示第一列(用戶 ID),作為關(guān)聯(lián)數(shù)組a的鍵。a[$1]++表示數(shù)組值自增(初始為 0,首次訪問后變?yōu)?1)。!a[$1]表示當(dāng)數(shù)組值為 0 時(shí)(首次出現(xiàn)),條件為真,執(zhí)行默認(rèn)操作(輸出當(dāng)前行)。
場(chǎng)景 2:部分字段去重(保留最后一次出現(xiàn)的行)
延續(xù)上述場(chǎng)景,需按用戶 ID 去重,保留最后一次出現(xiàn)的行:
命令:
awk '{a[$1]=$0} END{for(k in a) print a[k]}' user.txt輸出結(jié)果:
101 Charlie 102 Eve 103 Dave
原理解析:
- 遍歷文件時(shí),用數(shù)組
a存儲(chǔ)每個(gè) ID 對(duì)應(yīng)的最新行(重復(fù) ID 會(huì)覆蓋之前的值)。 END塊在文件遍歷結(jié)束后執(zhí)行,遍歷數(shù)組并輸出所有鍵對(duì)應(yīng)的行(順序不固定,如需排序可追加| sort)。
場(chǎng)景 3:全行去重(無需排序,保留首次出現(xiàn))
與 sort+uniq 功能一致,但無需排序,效率更高(時(shí)間復(fù)雜度 O(n)):
awk '!a[$0]++' data.txt
輸出結(jié)果(保留首次出現(xiàn)的行,順序與原文件一致):
apple banana orange
場(chǎng)景 4:按條件去重(如過濾特定字符后去重)
假設(shè) log.txt 內(nèi)容如下(需按日志時(shí)間戳去重,忽略后面的詳細(xì)信息):
2024-05-01 10:00:00 [INFO] 用戶登錄 2024-05-01 10:00:00 [ERROR] 數(shù)據(jù)庫(kù)連接失敗 2024-05-01 10:01:00 [INFO] 數(shù)據(jù)查詢成功
命令(按前 19 個(gè)字符(時(shí)間戳)去重,保留首次出現(xiàn)的行):
awk '!a[substr($0,1,19)]++' log.txt
輸出結(jié)果:
2024-05-01 10:00:00 [INFO] 用戶登錄 2024-05-01 10:01:00 [INFO] 數(shù)據(jù)查詢成功
原理:substr($0,1,19) 截取每行前 19 個(gè)字符(時(shí)間戳部分)作為數(shù)組鍵,實(shí)現(xiàn)按時(shí)間戳去重。
2.3 awk 去重的優(yōu)勢(shì)
- 無需排序:直接遍歷文件一次即可完成去重,效率高于
sort+uniq(尤其對(duì)于超大文件)。 - 靈活定制:支持按任意字段、字符長(zhǎng)度、正則表達(dá)式等條件去重,滿足復(fù)雜場(chǎng)景需求。
- 保留原始順序:默認(rèn)保留首次出現(xiàn)的行的順序,而
sort+uniq會(huì)改變行的順序(除非后續(xù)再排序回原始順序,操作繁瑣)。
三、精準(zhǔn)去重:sed 命令(適合小規(guī)模文件)
sed 是流式文本編輯器,通過正則表達(dá)式匹配和替換實(shí)現(xiàn)去重,適合小規(guī)模文件或簡(jiǎn)單的去重場(chǎng)景(性能略遜于 awk,但語(yǔ)法簡(jiǎn)潔)。
3.1 核心原理
利用 sed 的「保持空間(hold space)」和「模式空間(pattern space)」,將已處理的行存儲(chǔ)在保持空間中,遍歷每行時(shí)與保持空間中的內(nèi)容對(duì)比,若存在則刪除,不存在則添加到保持空間并輸出。
3.2 基本用法
場(chǎng)景:全行去重(保留首次出現(xiàn)的行,順序不變)
sed -n ':a; /^\n*$/!{H;g;s/\n//g;/:$/!s/$/:/;s/:[^:]*\(\1\)/:\1/;t b;g}; p; :b; s/:$//;h;$p' data.txt簡(jiǎn)化版本(適合無特殊字符的文件):
sed '$!N; /^\(.*\)\n\1$/!P; D' data.txt
示例:data.txt 原始內(nèi)容
apple banana apple orange banana
輸出結(jié)果(保留首次出現(xiàn)的行,順序不變):
apple banana orange
3.3 注意事項(xiàng)
sed去重語(yǔ)法較復(fù)雜,尤其對(duì)于部分字段去重場(chǎng)景,不如awk靈活。- 適合小規(guī)模文件(幾 MB 以內(nèi)),大規(guī)模文件建議使用
awk或sort+uniq。
四、性能對(duì)比與場(chǎng)景選擇
4.1 三種方法性能對(duì)比(基于 1GB 文本文件,1000 萬行數(shù)據(jù))
| 方法 | 時(shí)間復(fù)雜度 | 執(zhí)行時(shí)間 | 優(yōu)勢(shì)場(chǎng)景 |
|---|---|---|---|
sort+uniq | O(n log n) | 約 30 秒 | 需排序、統(tǒng)計(jì)重復(fù)次數(shù)、簡(jiǎn)單全行去重 |
awk | O(n) | 約 10 秒 | 無需排序、部分字段去重、保留原始順序 |
sed | O(n²) | 約 120 秒 | 小規(guī)模文件、簡(jiǎn)單全行去重 |
4.2 場(chǎng)景選擇建議
- 簡(jiǎn)單全行去重且需排序:優(yōu)先使用
sort+uniq(語(yǔ)法簡(jiǎn)潔,易上手)。 - 復(fù)雜場(chǎng)景(部分字段、保留最后一次出現(xiàn)、無需排序):優(yōu)先使用
awk(靈活高效)。 - 小規(guī)模文件、臨時(shí)快速去重:可使用
sed(無需記憶復(fù)雜語(yǔ)法,適合應(yīng)急)。 - 超大文件(10GB+):推薦
awk或sort --parallel=4 uniq(sort加--parallel參數(shù)開啟多線程加速)。
五、實(shí)戰(zhàn)案例:日志文件去重與分析
假設(shè)我們有一個(gè) Nginx 訪問日志 access.log,內(nèi)容如下(部分行):
192.168.1.1 - - [01/May/2024:10:00:00 +0800] "GET /index.html HTTP/1.1" 200 1024
192.168.1.2 - - [01/May/2024:10:00:01 +0800] "POST /login HTTP/1.1" 401 512
192.168.1.1 - - [01/May/2024:10:00:02 +0800] "GET /index.html HTTP/1.1" 200 1024
192.168.1.3 - - [01/May/2024:10:00:03 +0800] "GET /api/data HTTP/1.1" 200 2048
192.168.1.1 - - [01/May/2024:10:00:04 +0800] "GET /index.html HTTP/1.1" 200 1024
需求 1:按 IP 去重,統(tǒng)計(jì)每個(gè) IP 的訪問次數(shù)
awk '{print $1}' access.log | sort | uniq -c | sort -nr輸出結(jié)果(按訪問次數(shù)降序排列):
3 192.168.1.1
1 192.168.1.2
1 192.168.1.3
需求 2:按 URL 去重,保留每個(gè) URL 最后一次訪問的日志
awk '{url=$7; a[url]=$0} END{for(k in a) print a[k]}' access.log | sort -k4輸出結(jié)果(按時(shí)間戳排序,保留每個(gè) URL 最后一次訪問記錄):
192.168.1.2 - - [01/May/2024:10:00:01 +0800] "POST /login HTTP/1.1" 401 512 192.168.1.1 - - [01/May/2024:10:00:04 +0800] "GET /index.html HTTP/1.1" 200 1024 192.168.1.3 - - [01/May/2024:10:00:03 +0800] "GET /api/data HTTP/1.1" 200 2048
需求 3:去除重復(fù)的成功訪問日志(狀態(tài)碼 200),保留首次出現(xiàn)的行
awk '$9==200 && !a[$1$7]++' access.log
輸出結(jié)果(按 IP+URL 去重,僅保留首次成功訪問的日志):
192.168.1.1 - - [01/May/2024:10:00:00 +0800] "GET /index.html HTTP/1.1" 200 1024 192.168.1.3 - - [01/May/2024:10:00:03 +0800] "GET /api/data HTTP/1.1" 200 2048
六、總結(jié)
Linux Shell 提供了多種文件去重方案,核心可分為三類:
sort+uniq:基礎(chǔ)通用,適合簡(jiǎn)單全行去重、統(tǒng)計(jì)重復(fù)次數(shù),需排序支持。awk:高階靈活,適合復(fù)雜場(chǎng)景(部分字段、保留特定行、無需排序),效率高。sed:簡(jiǎn)潔輕便,適合小規(guī)模文件和臨時(shí)去重需求。
到此這篇關(guān)于從基礎(chǔ)到高階詳解Linux Shell文件去重的完整指南的文章就介紹到這了,更多相關(guān)Shell文件去重內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Linux下Tomcat啟動(dòng)正常,但瀏覽器無法訪問的解決方法
下面小編就為大家?guī)硪黄狶inux下Tomcat啟動(dòng)正常,但瀏覽器無法訪問的解決方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
linux?shell文件轉(zhuǎn)碼iconv命令的使用
本文主要介紹了linux?shell文件轉(zhuǎn)碼iconv命令的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Shell腳本實(shí)現(xiàn)硬盤空間和表空間的使用情況統(tǒng)計(jì)并郵件通知
這篇文章主要介紹了Shell腳本實(shí)現(xiàn)硬盤空間和表空間的使用情況統(tǒng)計(jì)并郵件通知,其中表空間是指oracle數(shù)據(jù)庫(kù)里的表空間,需要的朋友可以參考下2014-12-12

