Unix/Linux 文本处理利器:常用过滤器 (Unix Text Processing Filters)

Unix/Linux 文本处理利器:常用过滤器指南

在 Unix/Linux 环境下,一切皆文件。高效处理文本数据是系统管理、日志分析和日常开发的核心技能。本文总结了最高频率使用的文本“过滤器”命令,助你快速掌握管道 (Pipeline) 哲学的精髓。


一、 核心搜索与匹配 (Search & Match)

搜索是处理文本的第一步,grep 是其中绝对的王者。

1. grep (Global Regular Expression Print)

  • 功能:在输入流或文件中搜索符合特定模式 (Pattern) 的行,并将匹配行输出。
  • 核心记忆点:它是按 处理的过滤器。
选项作用记忆逻辑 (Mnemonic)注意事项
-E使用扩展正则表达式 (Extended Regex)Extended必记。建议默认开启,否则 +, ?, | 等符号需转义。
-F固定字符串匹配 (Fixed)Fixed必考。取消模式中所有正则符号的特殊含义,视为纯文本。
-P使用 Perl 兼容正则Perl最强正则。支持非贪婪匹配、零宽断言 (Lookahead) 等高级功能。
-i忽略大小写Ignore case匹配 error 同时也能匹配 ErrorERROR
-v反向匹配,打印匹配的行InVert用于过滤掉已知无关信息。
-c只打印匹配行的数量Count仅输出统计数字,不显示行内容。
-w强制匹配整个单词Word避免 cat 误匹配到 category
-n显示匹配行所在的行号Number方便快速定位。
-l只输出匹配的文件名List files查找存在指定模式的文件名,不显示内容。
-r递归搜索目录Recursivegrep -r "pattern" ./dir 在目录下所有文件中找。
-A / -B / -C打印上下文行After/ Before/ Context-C 2 打印匹配行及前后各两行 (看日志报错上下文必备)。

💡 技术贴士:grep -F 的“全文字面匹配”
当你需要搜索包含 .*[$ 等正则特殊字符,且不想写一堆反斜杠转义时,使用 -F

  • 常规做法grep "127\.0\.0\.1" (点号需转义)
  • -F 做法grep -F "127.0.0.1" (所有字符均视为普通字符,速度也更快)。

💡 技术贴士:grep -P 的“正则天花板”
-E (ERE) 无法满足需求(如需要非贪婪匹配或逻辑断言)时,请祭出 -P

  • 非贪婪匹配grep -P "a.*?b" (匹配到第一个 b 就停,而不是最后一个)。
  • 零宽断言grep -P '(?<=User: )\w+' (只提取 User: 后面的用户名,但不包含 User: 本身)。

使用示例:

  • 样例文本 (logs.txt)
    1
    2
    3
    10:01 INFO User login: admin
    10:02 ERROR DB connection failed
    10:03 INFO Data saved
  • 样例命令grep "ERROR" logs.txt
  • 样例输出
    1
    10:02 ERROR DB connection failed

二、 文本切片与提取 (Slicing)

用于从庞大的文本流中提取特定的部分(按行或按列)。

2. cut

  • 功能:按 (字段) 提取文本内容。
  • 核心记忆点:处理表格化数据(如 CSV, /etc/passwd)。
选项作用记忆逻辑 (Mnemonic)注意事项
-f指定提取字段 (Fields)Field从 1 开始计数。支持列表和范围,如 -f 1,3-5
-d指定列的分隔符 (Delimiter)Delimiter默认为 Tab。处理 CSV 需显式用 -d ','
-c按字符位置提取Character-c 1-5 提取每行前 5 个字符。
--complement补集提取Complement提取除指定列以外的所有列。

💡 技术贴士:cut 的强迫症(列排序坑)
无论你指定的字段顺序如何,cut 永远按照原文件中的列顺序输出
例如:cut -f 3,1 filecut -f 1,3 file 的输出结果是一模一样的(都是先输出第1列,再第3列)。它不能用于交换列的顺序!(需排版组合请用 awk

使用示例:

  • 样例文本 (user.csv)
    1
    2
    3
    id,name,email
    1,Alice,alice@example.com
    2,Bob,bob@example.com
  • 样例命令cut -d ',' -f 2 user.csv
  • 样例输出
    1
    2
    3
    name
    Alice
    Bob

3. head / tail

  • 功能:分别输出输入的 前 N 行后 N 行
选项作用记忆逻辑 (Mnemonic)注意事项
-n指定显示的行数Numberhead -n 5。若不指定,默认通常显示 10 行。
-n +K从第 K 行开始输出到末尾 (tail特有)核心考点tail -n +2 file.csv 常用于 跳过 CSV 表头
-f实时追踪文件增长 (tail 特有)Follow常用于实时查看日志。

三、 字符级转换 (Transformation)

4. tr (Translate)

  • 功能:基于 字符 (而非单词) 进行替换、删除或压缩。
  • 核心记忆点tr 不接受文件名作为参数,必须通过管道 | 或重定向 < 接收输入。
选项作用记忆逻辑 (Mnemonic)注意事项
-d删除匹配字符集中的字符Deletetr -d '0-9' 删除所有数字。
-s将连续重复字符压缩为一个Squeezetr -s ' ' 将多个连续空格压制为一个。
-c对字符集取反 (Complement)Complement操作除指定字符集 以外 的字符。

💡 技术贴士:tr 的“固执”设计

  • 纯粹的过滤器tr 是 Unix 中极少数完全不支持文件名参数的命令。它没有 tr 'a' 'b' file.txt 这种用法,必须配合管道 |< 使用。
  • 字符 vs 单词tr 处理的是 字符级 映射。tr 'apple' 'ABCDE' 并不是替换单词,而是把 a 换成 A,p 换成 B… 如果你需要处理单词,请使用 sed

使用示例:

  • 示例 1:基础字符替换 (管道)
    echo "hello world" | tr 'a-z' 'A-Z' -> HELLO WORLD
  • 示例 2:文件输入与输出重定向 (核心用法)
    如果你想处理文件 file.txt 并保存到 output.txt
    1
    tr 'a-z' 'A-Z' < file.txt > output.txt
    • < file.txt 将文件内容喂给 tr 的标准输入。
    • > output.txt 将处理后的结果保存。
  • 示例 3:压缩连续空格并删除数字
    1
    cat data.txt | tr -s ' ' | tr -d '0-9'
    (利用管道连接多个 tr 命令或从 cat 读取)

四、 统计、排序与去重 (Aggregation & Sorting)

这三个命令通常组合使用,构建小型的数据处理流水线。

5. wc (Word Count)

  • 功能:统计行数、单词数和字节数。
选项作用记忆逻辑 (Mnemonic)注意事项
-l只统计行数Lines统计查询结果总数时最常用。
-w只统计单词数Words以空白符为分隔依据。
-c只统计字节数Character/Byte

💡 技术贴士:wc 的输出细节

  • 作为命令参数wc -l file.txt 会输出 10 file.txt (数字 + 文件名)。
  • 作为管道过滤cat file.txt | wc -l 只输出 10 (仅数字)。
  • 处理多文件wc -l *.txt 会列出每个文件的行数,并在最后给出一个 total 总计。

6. sort

  • 功能:对文本行进行排序。
  • 核心记忆点:默认按 字典序 (ASCII) 排序。
选项作用记忆逻辑 (Mnemonic)注意事项
-n数值 大小排序Numeric从小到大。处理数值数据时必须加此参数。
-r逆序排列 (总)Reverse将整个排序结果反转。
-k指定排序关键字 (Key)Key极为强大,支持字段范围、字符偏移和每列独立设置。
-t指定列分隔符Tag / SeparaTor默认为非空白到空白的转换。CSV 通常需 -t ','
-f忽略大小写Fold case
-u排序并去重Unique效果等同于 sort | uniq,但效率更高。
-s稳定排序Stable保持值相同行的原始相对顺序。多列分阶段排序时强烈建议开启。

💡 技术贴士:sort -k 的进阶黑魔法

在 COMP9044 的实战中,-k 的用法远不止 k 2

  1. 指定范围 (-k FIELD_START,FIELD_END)
    • -k 2,2按第二列排序。
    • -k 2:从第二列开始,一直到行尾作为排序关键字。
  2. 每列独立参数
    • sort -k 1,1 -k 2,2rn:第一列按字典序升序,第二列按数值降序
    • 这里的 r (reverse) 和 n (numeric) 可以直接挂在数字后面,只对该列生效。
  3. 精准到字符 (-k F.C)
    • -k 1.3,1.5:按第一列的第 3 到第 5 个字符排序。

示例案例

  • 数据:Apple 10, Banana 5, Apple 5
  • 需求:先按第一列升序,第一列相同时按第二列数值降序。
  • 命令:sort -k 1,1 -k 2,2rn

7. uniq (Unique)

  • 功能:去除或统计 相邻 的重复行。
  • 核心记忆点使用前必须先排序 (sort)
选项作用记忆逻辑 (Mnemonic)注意事项
-c显示每行重复出现的次数Count构建“热点数据统计”常用此项。
-d只显示重复过的行Duplicate
-u只显示从未重复的行Unique
-i忽略大小写Ignore case
-f N比较时跳过前 N 个字段Fieldsuniq -f 1 (忽略第一列按空白符分割,只对比后面的内容)。

使用示例:

  • 样例文本 (fruits.txt)
    1
    2
    3
    4
    5
    6
    apple
    orange
    apple
    banana
    orange
    apple
  • 样例命令sort fruits.txt | uniq -c
  • 样例输出
    1
    2
    3
    3 apple
    1 banana
    2 orange

五、 流编辑器 (Stream Editor)

8. sed

  • 功能:通过编程方式编辑文本流(替换、删除、提取)。
  • 核心逻辑:读取一行 -> 执行命令 -> 打印结果(除非加 -n)。
选项/命令作用记忆逻辑 (Mnemonic)注意事项
s替换命令 (Substitute)Substitutes/old/new/ 基础结构。加 g 代表全局替换。
-E使用扩展正则Extended核心必加:使 (), `
-n静默模式No printing常配合 p 命令,如 sed -n 's/A/B/p' (仅打印替换成功的行)。
p打印命令Print需要显示指定行时使用。
d删除命令Delete移除符合条件的行。
-i直接修改原文件In-place慎用。通常调试好后再添加此参数。

💡 进阶:正则表达式兼容性与实战技巧

在 COMP9044 的实战或考试中,理解 sed 的正则特性是拿高分的关键。

1. BRE vs ERE (基本 vs 扩展正则)

  • 默认 (BRE):如果你不加 -E,那么 ( ), |, + 必须加转义符号:\( \), \|, \+
  • 推荐使用 -E:加了 -E 后,代码更简洁:
    • sed 's/\(TODO\|FIXME\)//'
    • sed -E 's/(TODO|FIXME)//'

2. 自定义定界符 (避免“倾斜牙签症”)

  • s 命令不一定用 / 分离开。如果你处理含有路径或 / 的文本,可以用任意字符(如 :#)。
    • 示例:删除 C++ 注释 //
    • sed -E 's://\s*(TODO|FIXME).*$::' program.c (这里用 : 做分隔符,代码由于没有一堆 \/ 而变得极度清晰)。

3. 后向引用 (Backreferences)

  • 在替换部分使用 \1, \2 代表前面括号捕获的组。
    • 案例:将 #include "file.h" 改为 #include <file.h>
    • sed -E 's/^#include\s+"([^"]*)"/#include <\1>/' program.c

4. 地址寻址 (Addressing & Ranges)

  • 正则过滤sed -n '/^\s*extern/p' file (仅打印以 extern 开头的行)。
  • 行内范围处理sed '10,20d' (删除 10-20 行)。
  • 正则范围处理:使用 /pattern1/,/pattern2/ 处理区间。
    • 案例:删除整个 main 函数
    • sed '/^int main/,/^}/d' program.c

5. 常用符号 &

  • 在替换内容中,& 代表整个已匹配到的字符串
    • 场景:给所有数字两边加括号
    • echo "Age: 25" | sed 's/[0-9]\+/(&)/g' -> Age: (25)

使用示例:

  • 样例输入echo "I love Python" | sed 's/Python/Linux/'
  • 样例输出I love Linux

💡 核心原理:Sed 的“状态机”本质与空间管理

理解 sed 就像理解一个只有一个寄存器的 CPU。它有两个核心内存位:

  1. Pattern Space (模式空间)

    • “工作台”sed 每次读取一行,丢进模式空间。
    • 所有的 s///d 操作都是在这个空间里完成的。
    • 处理周期:读取行 -> 放入模式空间 -> 执行所有命令 -> 打印模式空间内容(如果没有 -n) -> 清空模式空间 -> 下一行。
  2. Hold Space (保持空间)

    • “备用寄存器”或“剪贴板”。它在处理不同行之间 不会被清空
    • 这让 sed 有了“记忆力”,从而能实现状态机逻辑。

常用的“空间搬运”命令:

命令动作形象记忆
hPattern Space 覆盖到 Hold Space复制到剪贴板
HPattern Space 追加到 Hold Space增加到剪贴板末尾
gHold Space 覆盖到 Pattern Space粘贴并替换当前行
GHold Space 追加到 Pattern Space在当前行后加一行粘贴内容
x交换 Pattern Space 和 Hold Space调换工作台和剪贴板

实战案例:状态机的逻辑体现

  • 反转文件的所有行 (类似 tac)
    sed -n '1!G; h; $p' file

    • 逻辑拆解
      1. 1!G:如果不是第一行,就把 Hold Space (之前存的所有行) 贴到当前行后面。
      2. h:把现在的全结果覆盖存回 Hold Space。
      3. $p:到最后一行时,打印结果。
    • 这个过程由于 Hold Space 跨行保存了数据,就像一个不断累积状态的机床。
  • 范围触发器 (/start/,/end/)
    这是 sed 最常用的隐式状态机。当匹配到 /start/ 时,sed 进入“ON”状态,对后续每一行执行操作,直到匹配到 /end/ 切换回“OFF”。


六、 文件系统与逻辑连接

9. find

  • 功能:在目录树中递归查找符合条件的文件。
  • 核心语法find [路径] [表达式]
选项作用记忆逻辑 (Mnemonic)注意事项
-name按文件名查找Name支持通配符,如 '*.log'(建议加引号防止 Shell 提前展开)。
-type按文件类型查找Typef 代表文件,d 代表目录。
-mtime按修改时间查找M-Time-1 表示 24 小时内修改过的。
-size按文件大小查找Size+1M 大于1MB;-10k 小于10KB等。
-exec对找到的文件执行命令Executefind . -name "*.tmp" -exec rm {} \;。与 xargs 作用类似。

💡 -type 怎么用?
你不能直接写 find -type f "filename"。正确的逻辑是:find [哪里找] -type [找什么类型] -name [叫什么名字]

  • 示例:在当前目录查找名为 test.py 的文件:find . -type f -name "test.py"

10. xargs

  • 功能:参数构建器。将标准输入转换为命令行参数。

使用示例:

  • 场景 1:基础用法 (参数在末尾)
    查找并删除:find . -name "*.tmp" | xargs rm
  • 场景 2:进阶用法 (使用 -I 指定位置)
    如果你想把找到的文件移动到 backup 目录,rm 只接受末尾参数,但 mv 需要把文件放在中间:
    find . -name "*.log" | xargs -I {} mv {} ./backup/
    (这里的 {} 是占位符,代表管道传过来的每一个文件名)

七、 实战避坑指南 (Common Pitfalls)

在 COMP9044 的实验和考试中,以下三个“坑”最容易扣分:

1. cut 无法处理连续空格

cut 非常死板。如果数据是 1 Alice(中间多个空格),cut -d ' ' -f 2 会得到一个空字符串。

  • 解决方案:先用 tr -s ' ' 压缩空格,或者改用强大的 awk
    • ls -l | cut -d ' ' -f 5 (报错或结果不对)
    • ls -l | tr -s ' ' | cut -d ' ' -f 5

2. find 的通配符必须加引号

如果你执行 find . -name *.txt,Shell 会在 find 运行前就把 *.txt 展开成当前目录的文件名。如果当前目录有多个 txt,find 会报 paths must precede expression 错误。

  • 金律永远find-name 参数加引号:find . -name "*.txt"

3. uniq 必须紧跟在 sort 后面

uniq 只能去除相邻的重复行。

  • 错误示例:输入 A, B, A,直接 uniq 还是 A, B, A
  • 正确写法sort file | uniq

4. 文件名带空格的致命弱点 (find + xargs 最强闭坑)

如果文件名叫 my file.txt,默认的 xargs 会把空格当作分隔符,当成 myfile.txt 两个文件去传参,导致执行失败或误删。

  • 完美解决方案:让 find 以 Null 字符 \0 结尾输出,让 xargs 也以 \0 为分隔符接收:
    find . -name "*.txt" -print0 | xargs -0 rm

总结:管道 (Pipeline) 组合拳

这就是 Unix 的哲学:每个工具只做好一件事,通过管道连接它们。

综合练习:统计访问日志中 TOP 10 的 IP 地址
cat access.log | cut -d ' ' -f 1 | sort | uniq -c | sort -rn | head -n 10