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 同时也能匹配 Error 或 ERROR。 |
-v | 反向匹配,打印不匹配的行 | InVert | 用于过滤掉已知无关信息。 |
-c | 只打印匹配行的数量 | Count | 仅输出统计数字,不显示行内容。 |
-w | 强制匹配整个单词 | Word | 避免 cat 误匹配到 category。 |
-n | 显示匹配行所在的行号 | Number | 方便快速定位。 |
-l | 只输出匹配的文件名 | List files | 查找存在指定模式的文件名,不显示内容。 |
-r | 递归搜索目录 | Recursive | grep -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
310: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 file和cut -f 1,3 file的输出结果是一模一样的(都是先输出第1列,再第3列)。它不能用于交换列的顺序!(需排版组合请用awk)
使用示例:
- 样例文本 (
user.csv):1
2
3id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com - 样例命令:
cut -d ',' -f 2 user.csv - 样例输出:
1
2
3name
Alice
Bob
3. head / tail
- 功能:分别输出输入的 前 N 行 或 后 N 行。
| 选项 | 作用 | 记忆逻辑 (Mnemonic) | 注意事项 |
|---|---|---|---|
-n | 指定显示的行数 | Number | 如 head -n 5。若不指定,默认通常显示 10 行。 |
-n +K | 从第 K 行开始输出到末尾 (tail特有) | 核心考点:tail -n +2 file.csv 常用于 跳过 CSV 表头。 | |
-f | 实时追踪文件增长 (tail 特有) | Follow | 常用于实时查看日志。 |
三、 字符级转换 (Transformation)
4. tr (Translate)
- 功能:基于 字符 (而非单词) 进行替换、删除或压缩。
- 核心记忆点:
tr不接受文件名作为参数,必须通过管道|或重定向<接收输入。
| 选项 | 作用 | 记忆逻辑 (Mnemonic) | 注意事项 |
|---|---|---|---|
-d | 删除匹配字符集中的字符 | Delete | 如 tr -d '0-9' 删除所有数字。 |
-s | 将连续重复字符压缩为一个 | Squeeze | 如 tr -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:
- 指定范围 (
-k FIELD_START,FIELD_END):
-k 2,2:只按第二列排序。-k 2:从第二列开始,一直到行尾作为排序关键字。- 每列独立参数:
sort -k 1,1 -k 2,2rn:第一列按字典序升序,第二列按数值降序。- 这里的
r(reverse) 和n(numeric) 可以直接挂在数字后面,只对该列生效。- 精准到字符 (
-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 个字段 | Fields | 如 uniq -f 1 (忽略第一列按空白符分割,只对比后面的内容)。 |
使用示例:
- 样例文本 (
fruits.txt):1
2
3
4
5
6apple
orange
apple
banana
orange
apple - 样例命令:
sort fruits.txt | uniq -c - 样例输出:
1
2
33 apple
1 banana
2 orange
五、 流编辑器 (Stream Editor)
8. sed
- 功能:通过编程方式编辑文本流(替换、删除、提取)。
- 核心逻辑:读取一行 -> 执行命令 -> 打印结果(除非加 -n)。
| 选项/命令 | 作用 | 记忆逻辑 (Mnemonic) | 注意事项 |
|---|---|---|---|
s | 替换命令 (Substitute) | Substitute | s/old/new/ 基础结构。加 g 代表全局替换。 |
-E | 使用扩展正则 | Extended | 核心必加:使 (), ` |
-n | 静默模式 | No printing | 常配合 p 命令,如 sed -n 's/A/B/p' (仅打印替换成功的行)。 |
p | 打印命令 | 需要显示指定行时使用。 | |
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.c4. 地址寻址 (Addressing & Ranges)
- 正则过滤:
sed -n '/^\s*extern/p' file(仅打印以 extern 开头的行)。- 行内范围处理:
sed '10,20d'(删除 10-20 行)。- 正则范围处理:使用
/pattern1/,/pattern2/处理区间。
- 案例:删除整个
main函数sed '/^int main/,/^}/d' program.c5. 常用符号
&
- 在替换内容中,
&代表整个已匹配到的字符串。
- 场景:给所有数字两边加括号
echo "Age: 25" | sed 's/[0-9]\+/(&)/g'->Age: (25)
使用示例:
- 样例输入:
echo "I love Python" | sed 's/Python/Linux/' - 样例输出:
I love Linux
💡 核心原理:Sed 的“状态机”本质与空间管理
理解
sed就像理解一个只有一个寄存器的 CPU。它有两个核心内存位:
Pattern Space (模式空间):
- “工作台”。
sed每次读取一行,丢进模式空间。- 所有的
s///或d操作都是在这个空间里完成的。- 处理周期:读取行 -> 放入模式空间 -> 执行所有命令 -> 打印模式空间内容(如果没有
-n) -> 清空模式空间 -> 下一行。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!G:如果不是第一行,就把 Hold Space (之前存的所有行) 贴到当前行后面。h:把现在的全结果覆盖存回 Hold Space。$p:到最后一行时,打印结果。- 这个过程由于 Hold Space 跨行保存了数据,就像一个不断累积状态的机床。
范围触发器 (
/start/,/end/):
这是sed最常用的隐式状态机。当匹配到/start/时,sed进入“ON”状态,对后续每一行执行操作,直到匹配到/end/切换回“OFF”。
六、 文件系统与逻辑连接
9. find
- 功能:在目录树中递归查找符合条件的文件。
- 核心语法:
find [路径] [表达式]
| 选项 | 作用 | 记忆逻辑 (Mnemonic) | 注意事项 |
|---|---|---|---|
-name | 按文件名查找 | Name | 支持通配符,如 '*.log'(建议加引号防止 Shell 提前展开)。 |
-type | 按文件类型查找 | Type | f 代表文件,d 代表目录。 |
-mtime | 按修改时间查找 | M-Time | -1 表示 24 小时内修改过的。 |
-size | 按文件大小查找 | Size | +1M 大于1MB;-10k 小于10KB等。 |
-exec | 对找到的文件执行命令 | Execute | 如 find . -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 会把空格当作分隔符,当成 my 和 file.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