文件查找、文本及日志处理
前置知识:终端、路径与文件系统
本章目录
本章学习目标
- 区分文件查找与内容匹配
- 组合 grep、sort、uniq 和 awk
- 识别文本工具处理复杂 CSV 的边界
找文件与找文本
find 遍历目录并按名称、类型或时间筛选路径,grep 搜索文件内容。find "$WORK" -type f -name '*.log' 找日志文件,名称模式必须引用,避免先被当前目录的 Shell 展开。grep -n 'ERROR' logfile 找包含字样的行并显示行号,行号是文件位置,不是发生时间;多文件输出应同时保留文件名。
grep -F 按固定文本匹配,grep -E 使用扩展正则。点号在正则中匹配任意字符,固定文本的点号则只有字面含义。大小写、编码和换行方式会影响结果,不应该看到没匹配就认定事件没发生。grep 返回零表示有匹配,一表示没有匹配,更大状态表示错误,脚本要按用途处理。
从日志逐步计算频次
示例有四行简化日志:INFO、ERROR、ERROR、WARN。awk 按空白切列,第二列是级别;sort 把相同级别放在一起,uniq -c 统计相邻重复,得到 ERROR 两次,其余各一次。uniq 本身只合并相邻相同行,省略 sort 时散布在文件中的相同级别不会被正确累计。
LC_ALL=C 固定排序规则,避免不同本地语言环境影响顺序。head 看前几行,tail 看后几行,tail -f 持续跟随日志,需要人工中断,不用于本章自动脚本。统计需说明只分析哪些文件和时间窗口,轮转日志重复或漏收可能造成频次误差。输出排序用 sort -k 指定列时,要考虑数值与文本排序差异。
awk 的字段与模式
awk 对每行执行模式与动作,$0 是整行,$1 是第一字段,NF 是字段数,NR 是总行号。示例 awk '$2=="ERROR" {print $1,$3}' 输出错误时间和描述。外层单引号让 Bash 不把美元变量解释成自己的位置参数,awk 才能拿到字段引用。双引号错误包裹 awk 脚本是初学者常见故障。
awk -F, 能处理不含嵌套引号的简单逗号文本,却不是真正的 CSV 解析器。CSV 中引号字段可包含逗号或换行,简单切分会错位;教学数据的复杂读写应使用 pandas 或 Python csv。日志格式不稳定时也应先定义解析规则与异常行计数,不能让工具忽略坏行后输出一个看似精确的数字。
安全处理路径与检查异常
文件名可能含空格或换行,批量处理宜用 find -print0 搭配支持空分隔的读取或 xargs -0,不使用 for f in $(find ...)。命令替换会发生词拆分,使一个文件变成多个参数。find -exec 程序 {} + 可以安全批量传递文件路径,操作前仍需确认遍历根目录。
本章只读自己生成的小日志,不访问系统日志或用户文件。结果检查既要核对错误行数,也要核对总行数与格式异常。对于大文件,逐行工具适合流式分析;若需要复杂连接、窗口或完整 CSV 语义,用数据库和 pandas。选择工具依据数据结构与问题,不是依据哪条命令最短。
执行环境与验证边界
本课程命令使用 GNU 工具和 Bash 语法。Windows PowerShell 中的变量、引号、重定向和程序解析与 Bash 并不相同;请在 WSL 的 Ubuntu 终端运行示例,不能把整段 Bash 直接粘贴进 PowerShell。Git Bash 可以运行部分命令,但进程、权限、用户和工具选项可能不同;在 Git Bash 上成功不能证明已经在 Ubuntu 上验证。安装 WSL 是读者的环境准备步骤,课程脚本不会替读者更改系统配置。
每个可运行示例从脚本自身位置确定课程根目录,并只在课程 outputs/linux 子目录或自己建立的临时目录操作。首次运行前先阅读脚本,辨认哪些语句读取、哪些写入、哪些清理。检查路径中有空格时是否仍能运行,检查失败后是否返回非零状态,检查重新运行是否得到同样的结果。对生产数据进行操作时不能照搬练习目录的删除命令,必须重新确认目标范围。
Linux 学习应关注输入、输出、错误输出和退出状态这四件事。标准输出用于数据,标准错误用于诊断,退出状态用于让上层脚本判断成功与否;三者不能互相替代。把手工执行过程写成小脚本后,再通过独立目录重复运行一次。遇到差异时记录操作系统、Bash 和工具版本,把环境限制和代码错误区分开,这比无条件写下“全部通过”更有用。
完整示例与实际输出
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
WORK="$ROOT/outputs/linux/linux-text"
mkdir -p -- "$WORK"
log_file="$WORK/sample.log"
printf '%s\n' '09:00 INFO started' '09:01 ERROR timeout' '09:02 ERROR retry' '09:03 WARN slow' > "$log_file"
LC_ALL=C; export LC_ALL
find "$WORK" -maxdepth 1 -type f -name '*.log' -print
grep -nF 'ERROR' "$log_file"
awk '{print $2}' "$log_file" | sort | uniq -c
awk '$2=="ERROR" {print $1,$3}' "$log_file"
test "$(grep -cF 'ERROR' "$log_file")" -eq 2
实际运行通过Git Bash/MSYS on Windows compatibility run; Ubuntu and remote behavior not verified.
/d/liuwei/求职/笔试/notes/data_skills_html/outputs/linux/linux-text/sample.log
2:09:01 ERROR timeout
3:09:02 ERROR retry
2 ERROR
1 INFO
1 WARN
09:01 timeout
09:02 retry五道自测:先回答,再核对
01 · find 与 grep 分别找什么?
查看提示
路径与内容。
查看参考答案
find 找文件路径与元信息,grep 找内容匹配行。
02 · grep -F 的点号匹配任意字符吗?
查看提示
固定文本。
查看参考答案
不,固定匹配字面点号;正则点号才表示任意字符。
03 · uniq 可统计不相邻重复吗?
查看提示
相邻规则。
查看参考答案
不能,通常先 sort 再 uniq -c。
04 · awk 外层为何通常单引号?
查看提示
避免 Bash 解释 $1。
查看参考答案
让 awk 收到原始字段变量,避免 Shell 提前展开。
05 · awk -F, 能可靠解析全部 CSV 吗?
查看提示
引号里的逗号。
查看参考答案
不能,复杂 CSV 用 csv 模块或 pandas。
两项动手任务
任务 1 · 生成 INFO、ERROR、INFO 三行,统计级别频次。
提示
先排序再 uniq。
完整参考解答与检查方式
tmp_file=$(mktemp); printf '%s\n' 'INFO' 'ERROR' 'INFO' > "$tmp_file"; LC_ALL=C sort "$tmp_file" | uniq -c; rm -f -- "$tmp_file" 输出 ERROR 一次、INFO 两次;临时文件唯一创建。任务 2 · 查课程输出目录中的 log 文件,安全打印其行数。
提示
find -exec 传路径。
完整参考解答与检查方式
在课程根目录的 Bash 中执行 find './outputs/linux' -type f -name '*.log' -exec wc -l -- {} +。花括号由 find 替换成完整文件参数,空格文件名不会被拆开;默认打印文件名用于审计。官方资料与掌握状态
完成两项任务后,按独立完成程度记录。状态会尽量保存在当前浏览器,可导出备份。
动手任务记录
掌握状态与复习
关掉资料独立完成后记 A;提示后完成记 B;需要重学记 C。
记录尽量保存在当前浏览器,建议定期导出备份。