最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux使用uniq命令去除重復行的技巧分享

 更新時間:2026年03月02日 09:26:10   作者:Jinkxs  
在 Linux 的世界里,文本處理是日常運維、數據分析、日志排查等工作中最頻繁的操作之一,而 uniq 命令作為 GNU coreutils 中的一員,雖然看似簡單,卻蘊藏著強大的去重能力,本文將帶你深入掌握 uniq 的各種用法,需要的朋友可以參考下

在 Linux 的世界里,文本處理是日常運維、數據分析、日志排查等工作中最頻繁的操作之一。而 uniq 命令作為 GNU coreutils 中的一員,雖然看似簡單,卻蘊藏著強大的去重能力。很多人誤以為 uniq 可以直接“智能”地刪除所有重復行——其實不然!它要求輸入必須已排序才能正確識別相鄰重復項。本文將帶你深入掌握 uniq 的各種用法,并通過 Java 實現對比其邏輯,輔以圖表和實用鏈接,讓你徹底吃透這個經典命令。

什么是 uniq?

uniq 是一個用于報告或忽略文件中重復行的命令行工具。它的核心作用是:僅對連續(xù)重復的行進行去重或計數。這意味著如果你有一個未排序的文件,直接使用 uniq 并不能達到全局去重的效果!

關鍵前提:輸入必須有序!

$ cat unsorted.txt
apple
banana
apple
cherry
banana

$ uniq unsorted.txt
apple
banana
apple   # ← 仍然出現!因為不連續(xù)
cherry
banana  # ← 仍然出現!

只有當數據經過 sort 排序后,uniq 才能發(fā)揮真正作用:

$ sort unsorted.txt | uniq
apple
banana
cherry

正確姿勢:sort file.txt | uniq

uniq 常用選項詳解

選項說明
-c在每行前顯示該行重復次數(count)
-d僅顯示重復的行(至少出現兩次)
-u僅顯示唯一的行(只出現一次)
-i忽略大小寫比較
-f N忽略前 N 個字段(以空白分隔)
-s N忽略前 N 個字符
-w N僅比較前 N 個字符

實戰(zhàn)演練:基礎去重

假設我們有一個日志文件 access.log,內容如下:

GET /home
POST /login
GET /home
GET /profile
POST /login
GET /home

我們想統(tǒng)計每個請求路徑的訪問頻次:

$ sort access.log | uniq -c
      3 GET /home
      1 GET /profile
      2 POST /login

輸出結果清晰展示了每個唯一行及其出現次數。

高級技巧:忽略字段與字符

有時我們并不關心整行是否相同,而是希望基于部分字段或字符進行去重。

示例:忽略前兩個字段

假設我們有如下數據:

2024-05-01 user123 login_success
2024-05-02 user123 login_failed
2024-05-03 user123 login_success
2024-05-04 user456 login_success

我們只想根據“用戶名 + 狀態(tài)”去重,忽略日期:

$ sort data.txt | uniq -f 1
2024-05-01 user123 login_success
2024-05-02 user123 login_failed
2024-05-04 user456 login_success

-f 1 表示跳過第一個字段(即日期),從第二個字段開始比較。

結合其他命令使用

uniq 經常與 sort、awk、grep、cut 等組合使用,構建強大的文本處理流水線。

案例:找出訪問最頻繁的 IP 地址

假設 nginx.log 格式為:

192.168.1.1 - - [01/May/2024:10:00:00] "GET /index.html"
192.168.1.2 - - [01/May/2024:10:01:00] "GET /about.html"
192.168.1.1 - - [01/May/2024:10:02:00] "GET /contact.html"

提取 IP 并統(tǒng)計:

$ awk '{print $1}' nginx.log | sort | uniq -c | sort -nr
      2 192.168.1.1
      1 192.168.1.2

再加一步取 Top 3:

$ awk '{print $1}' nginx.log | sort | uniq -c | sort -nr | head -3

數據可視化:mermaid 圖表展示處理流程

這個流程圖清晰展示了從原始數據到分析結果的完整鏈路,適用于大多數日志分析場景。

uniq 的局限性

盡管 uniq 強大,但它也有幾個明顯的短板:

  1. 依賴排序:必須先 sort,否則無法正確去重。
  2. 內存限制:超大文件排序可能耗盡內存。
  3. 不支持正則匹配:無法按模式模糊去重。
  4. 單行比較:無法跨行或結構化比較。

對于更復雜的去重需求,我們可以轉向腳本語言如 Python、Java 或使用數據庫。

Java 實現 uniq 邏輯

下面我們用 Java 編寫一個簡易版的 uniq 工具,支持 -c、-d、-u 三個常用參數。

Step 1: 定義命令行參數解析

import java.util.*;

public class SimpleUniq {
    private boolean count = false;
    private boolean showDuplicatesOnly = false;
    private boolean showUniqueOnly = false;

    public void parseArgs(String[] args) {
        for (int i = 0; i < args.length; i++) {
            switch (args[i]) {
                case "-c":
                    count = true;
                    break;
                case "-d":
                    showDuplicatesOnly = true;
                    break;
                case "-u":
                    showUniqueOnly = true;
                    break;
                default:
                    // 假設后續(xù)是文件名,這里簡化處理
                    break;
            }
        }
    }
}

Step 2: 實現核心去重邏輯

public void processLines(List<String> lines) {
    if (lines.isEmpty()) return;

    Map<String, Integer> lineCount = new LinkedHashMap<>();
    String prevLine = null;

    // 模擬 uniq 的“相鄰去重”邏輯
    for (String line : lines) {
        if (!line.equals(prevLine)) {
            lineCount.put(line, lineCount.getOrDefault(line, 0) + 1);
        } else {
            // 如果和上一行相同,增加計數
            int currentCount = lineCount.get(line);
            lineCount.put(line, currentCount + 1);
        }
        prevLine = line;
    }

    // 輸出結果
    for (Map.Entry<String, Integer> entry : lineCount.entrySet()) {
        String line = entry.getKey();
        int cnt = entry.getValue();

        if (showDuplicatesOnly && cnt < 2) continue;
        if (showUniqueOnly && cnt > 1) continue;

        if (count) {
            System.out.printf("%6d %s%n", cnt, line);
        } else {
            System.out.println(line);
        }
    }
}

注意:上述實現模擬的是“相鄰重復”的行為,而不是全局去重。真正的 uniq 不會跨行累計計數,除非重復行連續(xù)出現。

Step 3: 支持全局去重(類似 sort + uniq)

如果我們希望實現“全局去重”,可以修改邏輯:

public void globalDedup(List<String> lines) {
    Map<String, Integer> globalCount = new HashMap<>();

    for (String line : lines) {
        globalCount.put(line, globalCount.getOrDefault(line, 0) + 1);
    }

    for (Map.Entry<String, Integer> entry : globalCount.entrySet()) {
        String line = entry.getKey();
        int cnt = entry.getValue();

        if (showDuplicatesOnly && cnt < 2) continue;
        if (showUniqueOnly && cnt > 1) continue;

        if (count) {
            System.out.printf("%6d %s%n", cnt, line);
        } else {
            System.out.println(line);
        }
    }
}

Step 4: 主函數整合

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

public class SimpleUniq {

    // ... 上面定義的字段和方法 ...

    public static void main(String[] args) throws IOException {
        SimpleUniq app = new SimpleUniq();
        app.parseArgs(args);

        if (args.length == 0) {
            System.err.println("Usage: java SimpleUniq [-c] [-d] [-u] [file]");
            return;
        }

        List<String> lines;
        if (args.length == 1 || (args.length > 1 && !args[args.length - 1].startsWith("-"))) {
            String filename = args[args.length - 1];
            lines = Files.readAllLines(Paths.get(filename));
        } else {
            // 從標準輸入讀取
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            lines = reader.lines().collect(Collectors.toList());
            reader.close();
        }

        // 是否啟用全局去重?這里默認使用相鄰去重(模擬原生 uniq)
        // 如需全局去重,可先排序:
        // Collections.sort(lines);
        // 然后調用 globalDedup

        Collections.sort(lines); // 模擬 sort | uniq
        app.globalDedup(lines);
    }
}

Java vs Shell 性能對比

雖然 Java 實現功能強大、可擴展,但在處理純文本時,Shell 命令通常更快,因為:

  • sortuniq 是高度優(yōu)化的 C 程序
  • 流水線操作避免了中間文件 I/O
  • 系統(tǒng)調用開銷小

但在復雜業(yè)務邏輯、結構化解析、多條件過濾等場景下,Java 更具優(yōu)勢。

實際應用場景舉例

場景一:清理重復的配置項

你有一個 .env 文件,不小心復制粘貼導致重復:

DB_HOST=localhost
API_KEY=abc123
DB_HOST=localhost
DEBUG=true
API_KEY=abc123

執(zhí)行:

$ sort .env | uniq > .env.clean

得到干凈版本:

API_KEY=abc123
DB_HOST=localhost
DEBUG=true

場景二:分析用戶行為路徑

用戶訪問記錄:

userA -> home
userB -> product
userA -> cart
userA -> home
userC -> home

你想知道哪些用戶訪問了多次首頁:

$ grep "home" user_actions.log | cut -d' ' -f1 | sort | uniq -d
userA

場景三:合并多個日志文件并去重

$ cat log1.txt log2.txt log3.txt | sort | uniq > merged_unique.log

進階思考:uniq 的算法本質

uniq 的底層算法非常簡單:

  1. 讀取第一行,設為“當前行”
  2. 讀取下一行,與“當前行”比較
  3. 若相同 → 計數器+1(若啟用 -c),繼續(xù)
  4. 若不同 → 輸出“當前行”及計數,更新“當前行”為新行,計數器重置為1
  5. 循環(huán)直到 EOF

這種“滑動窗口”式的相鄰比較,時間復雜度為 O(n),空間復雜度為 O(1),效率極高。

uniq 與其他去重工具對比

工具是否需要排序是否支持全局去重是否支持計數適用場景
uniq? 是? 否? 是簡單相鄰去重
awk '!a[$0]++'? 否? 是? 是(需手動)全局去重,靈活
sort -u? 是? 是? 否快速全局去重
perl -ne 'print unless $seen{$_}++'? 否? 是? 否腳本化去重
Java/Python? 否? 是? 是復雜邏輯、大數據、結構化

自動化腳本示例

編寫一個 Shell 腳本 dedup.sh,自動對指定文件去重并備份原文件:

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

FILE=$1
BACKUP="${FILE}.bak"

if [ ! -f "$FILE" ]; then
    echo "Error: File '$FILE' not found."
    exit 1
fi

echo "Backing up $FILE to $BACKUP..."
cp "$FILE" "$BACKUP"

echo "Deduplicating and sorting $FILE..."
sort "$BACKUP" | uniq > "$FILE"

echo "Done. Original saved as $BACKUP"

賦予執(zhí)行權限:

chmod +x dedup.sh
./dedup.sh data.txt

測試你的理解:小測驗

  1. uniq 能否直接對無序文件去重?
  2. uniq -d 輸出的是什么?
  3. 如何讓 uniq 忽略大小寫?
  4. sort file.txt | uniq -c | sort -nr 的作用是什么?
  5. 為什么 uniq 不設計成自動排序?

批量處理多個文件

有時我們需要對目錄下所有 .log 文件分別去重:

for file in *.log; do
    echo "Processing $file..."
    sort "$file" | uniq > "${file%.log}.clean.log"
done

${file%.log} 是 Bash 參數擴展,用于移除后綴。

構建自己的 uniq 工具箱

你可以創(chuàng)建別名或函數,簡化常用操作:

# ~/.bashrc or ~/.zshrc

# 全局去重并計數
alias uniqc='sort | uniq -c'

# 顯示重復行
alias uniqd='sort | uniq -d'

# 顯示唯一行
alias uniqu='sort | uniq -u'

# 忽略大小寫去重
alias uniqi='sort -f | uniq -i'

然后 source ~/.bashrc 生效。

使用示例:

$ cat mixed.txt
Apple
apple
Banana
banana
Cherry

$ cat mixed.txt | uniqi
Apple
Banana
Cherry

常見錯誤與避坑指南

錯誤 1:忘記排序

$ uniq data.txt  # 無效!重復行仍存在

? 正確做法:

$ sort data.txt | uniq

錯誤 2:誤用-u和-d

$ echo -e "a\na\nb" | sort | uniq -u
b

很多人期望 -u 是“去重后的所有行”,其實它是“只出現一次的行”。

錯誤 3:字段分隔符誤解

-f N 是以空白字符(空格、制表符)為分隔符跳過字段。如果數據用逗號分隔,需先轉換:

$ sed 's/,/ /g' data.csv | sort | uniq -f 1

或使用 awk 更靈活處理:

$ awk -F',' '{print $2,$3}' data.csv | sort | uniq

uniq 的創(chuàng)意用法

1. 檢測文件是否完全相同

$ md5sum file1 file2 | sort | uniq -w 32

如果輸出只有一行,說明兩個文件內容一致。

2. 找出兩個文件的差異部分

$ sort file1 file2 | uniq -u

輸出的是只在一個文件中出現的行。

3. 生成唯一 ID 列表

$ awk '{print $2}' users.log | sort | uniq

提取第二列(假設是用戶ID),去重排序。

日志輪轉中的應用

在系統(tǒng)維護中,常需清理重復日志條目以節(jié)省空間:

# 清理并壓縮舊日志
zcat access.log.1.gz | sort | uniq | gzip > access.log.1.uniq.gz

uniq 與數據庫去重對比

雖然數據庫(如 MySQL、PostgreSQL)也能做 DISTINCTGROUP BY 去重,但 uniq 優(yōu)勢在于:

  • 無需數據庫環(huán)境
  • 啟動快,零配置
  • 適合一次性、臨時性任務
  • 可無縫接入 Shell 腳本自動化

例如:

SELECT url, COUNT(*) FROM access_log GROUP BY url ORDER BY COUNT(*) DESC;

等價于:

awk '{print $7}' access.log | sort | uniq -c | sort -nr

uniq 在 DevOps 中的角色

在 CI/CD 流水線中,常需檢查依賴列表、鏡像標簽、部署配置是否有重復沖突:

# 檢查 requirements.txt 是否有重復包
sort requirements.txt | uniq -d
# 若有輸出,則存在重復依賴,需人工干預

性能優(yōu)化建議

  1. 大文件先壓縮再傳輸gzip + zcat 減少 I/O
  2. 使用 LC_ALL=C sort 加速排序(禁用本地化)
  3. 避免管道嵌套過深:可考慮臨時文件緩存中間結果
  4. 并行處理parallel + split 分片加速

示例:

export LC_ALL=C
sort bigfile.txt | uniq -c > result.txt

跨平臺兼容性

雖然 uniq 是 POSIX 標準命令,但在 macOS、BSD、Linux 上行為略有差異。例如:

  • macOS 的 sort 默認不支持 --parallel
  • 某些選項(如 -w)在舊版系統(tǒng)中可能缺失

建議在腳本頭部加入版本檢測:

if ! command -v uniq &> /dev/null; then
    echo "uniq could not be found"
    exit 1
fi

深入源碼:GNU uniq 的實現思路

如果你想閱讀 uniq 的 C 源碼,可從 GNU Coreutils 項目入手(雖不能提供 GitHub 鏈接,但可通過官網下載 tarball)。其核心邏輯圍繞:

  • readline() 逐行讀取
  • strcmp() 比較字符串
  • prevline 緩存上一行
  • 計數器控制輸出格式

精簡偽代碼:

char *prev = NULL;
int count = 0;

while (read_line(&current)) {
    if (prev == NULL || strcmp(prev, current) != 0) {
        if (prev != NULL) output(prev, count);
        prev = strdup(current);
        count = 1;
    } else {
        count++;
    }
}
output(prev, count); // flush last line

Java 版本增強:支持自定義比較器

我們可以讓 Java 版本支持忽略大小寫、忽略前綴等功能:

@FunctionalInterface
interface LineComparator {
    boolean isEqual(String a, String b);
}

// 默認精確匹配
LineComparator defaultComparator = String::equals;

// 忽略大小寫
LineComparator ignoreCaseComparator = (a, b) -> a.equalsIgnoreCase(b);

// 忽略前 N 字符
LineComparator ignorePrefixComparator(int n) {
    return (a, b) -> {
        if (a.length() <= n || b.length() <= n) return false;
        return a.substring(n).equals(b.substring(n));
    };
}

processLines 中傳入比較器即可靈活擴展。

教學案例:課堂練習題

讓學生完成以下任務:

  1. 創(chuàng)建包含重復行的文本文件
  2. 使用 sort + uniq 去重
  3. 使用 uniq -c 統(tǒng)計頻次
  4. 使用 uniq -d 找出高頻詞
  5. 用 Java 實現相同功能
  6. 對比兩者性能(time 命令)

學習路徑建議

如果你剛接觸 Linux 文本處理,建議按此順序學習:

  1. cat, head, tail —— 基礎查看
  2. grep —— 搜索過濾
  3. sort —— 排序
  4. uniq —— 去重計數
  5. awk, sed —— 高級處理
  6. xargs, find —— 批量操作
  7. Shell 腳本編程 —— 自動化

工具鏈整合:日志分析系統(tǒng)雛形

結合 uniq、cron、mail 可構建簡易監(jiān)控系統(tǒng):

#!/bin/bash
LOG="/var/log/app/error.log"
REPORT="/tmp/daily_report.txt"

echo "=== Daily Error Summary ===" > "$REPORT"
echo "" >> "$REPORT"

zcat "$LOG".*gz 2>/dev/null | cat - "$LOG" | \
    grep "$(date -d yesterday +%Y-%m-%d)" | \
    awk '{print $5}' | sort | uniq -c | sort -nr >> "$REPORT"

mail -s "Daily Error Report" admin@example.com < "$REPORT"

每天自動發(fā)送錯誤類型統(tǒng)計郵件。

社區(qū)與討論

雖然不能提供具體論壇鏈接,但你可以在主流技術社區(qū)搜索 “Linux uniq tutorial” 或 “uniq command examples”,通常 Stack Overflow、Reddit 的 r/linuxquestions、Linux 中國等都有豐富討論。

小測驗答案

  1. ? 不能,必須先排序。
  2. ? 僅顯示重復的行(出現 ≥2 次)。
  3. ? 使用 -i 選項,且通常配合 sort -f。
  4. ? 按出現頻次從高到低排序顯示。
  5. ? 因為排序代價高,且有時用戶只需相鄰去重;分開設計更靈活高效。

總結:uniq 的哲學

uniq 體現了 Unix 工具設計的核心哲學:

Do One Thing and Do It Well.

它不做排序,不做復雜匹配,只專注于“相鄰行去重”。通過管道與其他工具組合,卻能解決千變萬化的問題。這正是 Linux 命令行的魅力所在 —— 簡單、組合、強大。

掌握 uniq,不僅是學會一個命令,更是理解了如何用最小工具構建最大價值的思維方式。

下一步行動建議

  1. 打開終端,創(chuàng)建測試文件實操一遍
  2. 用 Java 實現完整功能并添加單元測試
  3. 嘗試在真實日志中應用 uniq 分析
  4. 編寫自動化腳本提升工作效率
  5. 探索 awksed 進階文本處理

記?。?strong>The shell is your superpower. 

以上就是Linux使用uniq命令去除重復行的技巧分享的詳細內容,更多關于Linux uniq去除重復行的資料請關注腳本之家其它相關文章!

相關文章

最新評論

古浪县| 灵台县| 通海县| 建德市| 来安县| 佛教| 辽阳县| 绵阳市| 古蔺县| 贺兰县| 通山县| 隆子县| 北海市| 沙雅县| 托克逊县| 咸宁市| 微山县| 抚州市| 永州市| 柘城县| 晋城| 澎湖县| 承德市| 九台市| 山丹县| 长沙县| 清徐县| 夹江县| 京山县| 延吉市| 和林格尔县| 康保县| 镇平县| 高青县| 东宁县| 木里| 始兴县| 巴里| 呈贡县| 慈溪市| 栾川县|