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

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

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

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

什么是 uniq?

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

關(guān)鍵前提:輸入必須有序!

$ cat unsorted.txt
apple
banana
apple
cherry
banana

$ uniq unsorted.txt
apple
banana
apple   # ← 仍然出現(xiàn)!因?yàn)椴贿B續(xù)
cherry
banana  # ← 仍然出現(xiàn)!

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

$ sort unsorted.txt | uniq
apple
banana
cherry

正確姿勢(shì):sort file.txt | uniq

uniq 常用選項(xiàng)詳解

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

實(shí)戰(zhàn)演練:基礎(chǔ)去重

假設(shè)我們有一個(gè)日志文件 access.log,內(nèi)容如下:

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

我們想統(tǒng)計(jì)每個(gè)請(qǐng)求路徑的訪問頻次:

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

輸出結(jié)果清晰展示了每個(gè)唯一行及其出現(xiàn)次數(shù)。

高級(jí)技巧:忽略字段與字符

有時(shí)我們并不關(guān)心整行是否相同,而是希望基于部分字段或字符進(jìn)行去重。

示例:忽略前兩個(gè)字段

假設(shè)我們有如下數(shù)據(jù):

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

我們只想根據(jù)“用戶名 + 狀態(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 表示跳過第一個(gè)字段(即日期),從第二個(gè)字段開始比較。

結(jié)合其他命令使用

uniq 經(jīng)常與 sort、awk、grep、cut 等組合使用,構(gòu)建強(qiáng)大的文本處理流水線。

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

假設(shè) 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)計(jì):

$ 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

數(shù)據(jù)可視化:mermaid 圖表展示處理流程

這個(gè)流程圖清晰展示了從原始數(shù)據(jù)到分析結(jié)果的完整鏈路,適用于大多數(shù)日志分析場(chǎng)景。

uniq 的局限性

盡管 uniq 強(qiáng)大,但它也有幾個(gè)明顯的短板:

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

對(duì)于更復(fù)雜的去重需求,我們可以轉(zhuǎn)向腳本語言如 Python、Java 或使用數(shù)據(jù)庫。

Java 實(shí)現(xiàn) uniq 邏輯

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

Step 1: 定義命令行參數(shù)解析

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:
                    // 假設(shè)后續(xù)是文件名,這里簡化處理
                    break;
            }
        }
    }
}

Step 2: 實(shí)現(xiàn)核心去重邏輯

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 {
            // 如果和上一行相同,增加計(jì)數(shù)
            int currentCount = lineCount.get(line);
            lineCount.put(line, currentCount + 1);
        }
        prevLine = line;
    }

    // 輸出結(jié)果
    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);
        }
    }
}

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

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

如果我們希望實(shí)現(xiàn)“全局去重”,可以修改邏輯:

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: 主函數(shù)整合

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 {
            // 從標(biāo)準(zhǔn)輸入讀取
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            lines = reader.lines().collect(Collectors.toList());
            reader.close();
        }

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

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

Java vs Shell 性能對(duì)比

雖然 Java 實(shí)現(xiàn)功能強(qiáng)大、可擴(kuò)展,但在處理純文本時(shí),Shell 命令通常更快,因?yàn)椋?/p>

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

但在復(fù)雜業(yè)務(wù)邏輯、結(jié)構(gòu)化解析、多條件過濾等場(chǎng)景下,Java 更具優(yōu)勢(shì)。

實(shí)際應(yīng)用場(chǎng)景舉例

場(chǎng)景一:清理重復(fù)的配置項(xiàng)

你有一個(gè) .env 文件,不小心復(fù)制粘貼導(dǎo)致重復(fù):

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

場(chǎng)景二:分析用戶行為路徑

用戶訪問記錄:

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

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

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

場(chǎng)景三:合并多個(gè)日志文件并去重

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

進(jìn)階思考:uniq 的算法本質(zhì)

uniq 的底層算法非常簡單:

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

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

uniq 與其他去重工具對(duì)比

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

自動(dòng)化腳本示例

編寫一個(gè) Shell 腳本 dedup.sh,自動(dòng)對(duì)指定文件去重并備份原文件:

#!/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í)行權(quán)限:

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

測(cè)試你的理解:小測(cè)驗(yàn)

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

批量處理多個(gè)文件

有時(shí)我們需要對(duì)目錄下所有 .log 文件分別去重:

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

${file%.log} 是 Bash 參數(shù)擴(kuò)展,用于移除后綴。

構(gòu)建自己的 uniq 工具箱

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

# ~/.bashrc or ~/.zshrc

# 全局去重并計(jì)數(shù)
alias uniqc='sort | uniq -c'

# 顯示重復(fù)行
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

常見錯(cuò)誤與避坑指南

錯(cuò)誤 1:忘記排序

$ uniq data.txt  # 無效!重復(fù)行仍存在

? 正確做法:

$ sort data.txt | uniq

錯(cuò)誤 2:誤用-u和-d

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

很多人期望 -u 是“去重后的所有行”,其實(shí)它是“只出現(xiàn)一次的行”。

錯(cuò)誤 3:字段分隔符誤解

-f N 是以空白字符(空格、制表符)為分隔符跳過字段。如果數(shù)據(jù)用逗號(hào)分隔,需先轉(zhuǎn)換:

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

或使用 awk 更靈活處理:

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

uniq 的創(chuàng)意用法

1. 檢測(cè)文件是否完全相同

$ md5sum file1 file2 | sort | uniq -w 32

如果輸出只有一行,說明兩個(gè)文件內(nèi)容一致。

2. 找出兩個(gè)文件的差異部分

$ sort file1 file2 | uniq -u

輸出的是只在一個(gè)文件中出現(xiàn)的行。

3. 生成唯一 ID 列表

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

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

日志輪轉(zhuǎn)中的應(yīng)用

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

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

uniq 與數(shù)據(jù)庫去重對(duì)比

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

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

例如:

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

等價(jià)于:

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

uniq 在 DevOps 中的角色

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

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

性能優(yōu)化建議

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

示例:

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

跨平臺(tái)兼容性

雖然 uniq 是 POSIX 標(biāo)準(zhǔn)命令,但在 macOS、BSD、Linux 上行為略有差異。例如:

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

建議在腳本頭部加入版本檢測(cè):

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

深入源碼:GNU uniq 的實(shí)現(xiàn)思路

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

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

精簡偽代碼:

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 版本增強(qiáng):支持自定義比較器

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

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

// 默認(rèn)精確匹配
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 中傳入比較器即可靈活擴(kuò)展。

教學(xué)案例:課堂練習(xí)題

讓學(xué)生完成以下任務(wù):

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

學(xué)習(xí)路徑建議

如果你剛接觸 Linux 文本處理,建議按此順序?qū)W習(xí):

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

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

結(jié)合 uniq、cron、mail 可構(gòu)建簡易監(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"

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

社區(qū)與討論

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

小測(cè)驗(yàn)答案

  1. ? 不能,必須先排序。
  2. ? 僅顯示重復(fù)的行(出現(xiàn) ≥2 次)。
  3. ? 使用 -i 選項(xiàng),且通常配合 sort -f。
  4. ? 按出現(xiàn)頻次從高到低排序顯示。
  5. ? 因?yàn)榕判虼鷥r(jià)高,且有時(shí)用戶只需相鄰去重;分開設(shè)計(jì)更靈活高效。

總結(jié):uniq 的哲學(xué)

uniq 體現(xiàn)了 Unix 工具設(shè)計(jì)的核心哲學(xué):

Do One Thing and Do It Well.

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

掌握 uniq,不僅是學(xué)會(huì)一個(gè)命令,更是理解了如何用最小工具構(gòu)建最大價(jià)值的思維方式。

下一步行動(dòng)建議

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

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

以上就是Linux使用uniq命令去除重復(fù)行的技巧分享的詳細(xì)內(nèi)容,更多關(guān)于Linux uniq去除重復(fù)行的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

甘孜县| 秦皇岛市| 肇庆市| 西吉县| 南投县| 洛扎县| 伊宁市| 合水县| 太仓市| 安阳市| 易门县| 桃源县| 启东市| 阆中市| 龙门县| 双流县| 巢湖市| 安福县| 商城县| 长宁区| 普陀区| 吉首市| 连州市| 南通市| 三明市| 新民市| 弋阳县| 方山县| 醴陵市| 宾阳县| 婺源县| 英吉沙县| 颍上县| 五常市| 麦盖提县| 来安县| 恩施市| 集贤县| 曲阜市| 巨鹿县| 三原县|