Linux 标准 IO(输入/输出)与日志文件处理
继上一篇《目录、配置文件与日志文件管理》(pwd、cp 等命令)之后,本篇讲解 Linux 中非常重要的一个基础概念——标准 IO,以及围绕它衍生出的重定向、管道和三大文本处理工具(grep / sed / awk)。这些内容是后续做日志分析、故障排查和 Shell 脚本编写的地基。
一、标准 IO:三种数据流
在 Linux 中,一切皆文件,程序的输入输出也被抽象为"数据流"。每执行一个命令,系统都会为其默认打开三个数据流,并用**文件描述符(File Descriptor, fd)**进行编号:
| 0 | 标准输入 | stdin (Standard Input) | 键盘 | 程序读取数据的来源 |
| 1 | 标准输出 | stdout (Standard Output) | 终端屏幕 | 程序正确结果的输出 |
| 2 | 标准错误 | stderr (Standard Error) | 终端屏幕 | 程序错误信息的输出 |

执行命令时,正确结果和错误结果默认都会显示在终端中,这是因为 stdout(1)和 stderr(2)的默认目的地都是终端设备。但它们本质是两条相互独立的流,可以分别重定向到不同的地方——这是日志分离、错误排查的基础。
验证示例:
# 同时产生标准输出和标准错误
ls /etc/passwd /notexist
输出中 ls: cannot access '/notexist': No such file or directory 走的是 stderr(fd 2),而 /etc/passwd 走的是 stdout(fd 1)。
二、输出重定向:>、>>、2>
重定向的本质:改变数据流的默认目的地,把原本输出到屏幕的内容转存到文件中。
1. >:覆盖式重定向标准输出
将命令的正确结果写入文件,文件原有内容会被清空覆盖。文件不存在则自动创建。
# 把目录清单保存到文件
ls -l /etc > etc_list.txt
# 清空一个文件(利用空输出的覆盖特性)
> app.log
⚠️ > 是覆盖写,生产环境对重要文件操作前务必确认,避免误清数据。
2. >>:追加式重定向标准输出
与 > 的唯一区别:不清空原内容,在文件末尾追加。日志记录场景几乎都用 >>。
# 持续记录系统负载,不覆盖历史数据
uptime >> /var/log/load_monitor.log
# 多次执行,内容逐行累积
date >> run.log
echo "任务执行完成" >> run.log
3. 2> / 2>>:重定向标准错误
符号前的数字就是文件描述符。2> 只捕获错误输出,正确结果仍显示在屏幕。
# 把报错信息单独存到错误日志
ls /notexist 2> error.log
# find 遍历全盘时权限不足的报错很多,丢弃到"黑洞"
find / -name "nginx.conf" 2> /dev/null
/dev/null 是 Linux 的"黑洞设备",写入它的任何数据都会被直接丢弃,常用于屏蔽无用输出。
重点理解 2>&1:它的含义是"让 fd 2 指向 fd 1 当前指向的地方"。下面两条命令顺序不同,结果完全不同:
./app.sh > all.log 2>&1 # ✅ 正确:stdout 先进文件,stderr 再跟随进文件
./app.sh 2>&1 > all.log # ❌ 错误:stderr 先跟随 stdout(此时还是屏幕),之后 stdout 才进文件
三、输入重定向:<
> A改变的是输出的去向,< 则相反:让命令不再从键盘读取输入,改为从文件中读取。
# 统计文件行数(注意:wc -l file 与 wc -l < file 输出格式略有差异)
wc -l < /etc/passwd
# 把文件内容作为邮件正文发送
mail -s "日报" admin@example.com < report.txt
# 批量创建用户时,从文件读入用户列表
while read user; do useradd "$user"; done < users.txt
补充:<<(Here Document,此处文档),在脚本中内联提供多行输入:
cat << EOF > /etc/motd
==============================
服务器已加固,请勿违规操作
==============================
EOF
四、管道 | 与 tee
1. 管道 |:命令之间的"传送带"
管道将前一个命令的标准输出作为后一个命令的标准输入,实现多个命令的串联协作。注意:管道只传递 stdout(fd 1),stderr 不会被传递。

# 查看监听中的 TCP 端口
ss -tlnp | grep LISTEN
# 统计当前系统有多少个 bash 登录会话
who | wc -l
# 多级管道:找出占用内存最高的前 5 个进程
ps aux –sort=-%mem | head -6
如需让错误输出也进入管道,先合并数据流:
./app.sh 2>&1 | grep "ERROR"
2. tee:一份数据,两处去向
tee 命令读取标准输入,同时输出到屏幕和文件——就像水管上的三通接头。需要"既看实时输出、又留档记录"时非常实用。

# 安装过程既在屏幕显示,又保存到日志
yum install nginx -y | tee install.log
# -a 选项:追加而非覆盖
./deploy.sh | tee -a deploy_history.log
# 配合 sudo 写入无权限的文件(经典用法:echo 本身没有权限,靠 tee 提权写入)
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
五、grep 与正则表达式:日志检索的利器
grep(Global Regular Expression Print)按模式逐行过滤文本,是日志分析中使用频率最高的命令。

1. 常用选项
| -i | 忽略大小写 | grep -i "error" app.log |
| -v | 反向匹配(显示不含关键字的行) | grep -v "^#" nginx.conf |
| -n | 显示匹配行的行号 | grep -n "failed" secure.log |
| -c | 只统计匹配的行数 | grep -c "404" access.log |
| -r | 递归搜索目录 | grep -r "password" /etc/ |
| -w | 整词匹配 | grep -w "root" /etc/passwd |
| -A n | 显示匹配行及其后 n 行 (After) | grep -A 3 "ERROR" app.log |
| -B n | 显示匹配行及其前 n 行 (Before) | grep -B 3 "ERROR" app.log |
| -E | 启用扩展正则(等价于 egrep) | `grep -E "404 |
2. 基础正则表达式(BRE)常用元字符
| ^ | 行首锚定 | grep "^root" /etc/passwd 匹配以 root 开头的行 |
| $ | 行尾锚定 | grep "bash$" /etc/passwd 匹配以 bash 结尾的行 |
| . | 匹配任意单个字符 | grep "r..t" file 可匹配 root、rust |
| * | 前一个字符出现 0 次或多次 | grep "ab*c" file |
| […] | 字符集合 | grep "[0-9]" file 匹配含数字的行 |
| [^…] | 字符集合取反 | grep -v "^#" nginx.conf | grep -v "^$" 过滤注释和空行 |
实战示例——分析 Nginx 访问日志:
# 找出所有 5xx 服务端错误
grep -E '" 5[0-9]{2} ' /var/log/nginx/access.log
# 统计今天 404 出现的次数
grep -c "404" /var/log/nginx/access.log
# 查看某个 IP 的全部访问记录
grep "192.168.10.100" /var/log/nginx/access.log
六、sed:流式文本编辑器
sed(Stream Editor)逐行读取文本,按规则进行增、删、改、查,适合批量、非交互式的文本修改。

1. 核心语法
sed [选项] '动作' 文件
2. 替换(最常用)
# 格式:s/旧内容/新内容/标志
# 只替换每行第一个匹配项(默认)
sed 's/error/ERROR/' app.log
# g 标志:替换每行所有匹配项
sed 's/8080/9090/g' nginx.conf
# -i:直接修改文件内容(不带 -i 时只在屏幕预览,不改原文件,建议先预览再加 -i)
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 修改前自动备份原文件为 .bak
sed -i.bak 's/8080/9090/g' nginx.conf
3. 删除与按行操作
# 删除第 5 行
sed '5d' file
# 删除所有空行
sed '/^$/d' file
# 删除所有注释行
sed '/^#/d' nginx.conf
# 只打印第 10~20 行(配合 -n 抑制默认输出)
sed -n '10,20p' app.log
4. 实战示例
# 快速查看配置文件有效内容(去掉注释和空行)
grep -v "^#" nginx.conf | grep -v "^$" | sed 's/^[ \\t]*//'
# 批量把日志中的手机号脱敏(扩展正则用 -r 或 -E)
sed -r 's/(1[0-9]{2})[0-9]{4}([0-9]{4})/\\1****\\2/g' user.log
七、awk:字段级文本处理与统计
awk 把每行文本按分隔符切分成字段(列),可精确取列、计算、汇总,是处理日志和报表的"瑞士军刀"。
**为分隔符,-F 可自定义
- $0 表示整行,$1、$2… 表示第 1、2… 列,$NF 表示最后一列
- NR 当前行号,NF 当前行的字段数

2. 取列(最常用)
# 打印第 1 列:查看所有用户名
awk -F: '{print $1}' /etc/passwd
# 打印第 1 列和第 3 列,自定义输出格式
awk -F: '{print "用户:" $1, "UID:" $3}' /etc/passwd
# 找出 UID 大于等于 1000 的普通用户(条件过滤)
awk -F: '$3 >= 1000 {print $1}' /etc/passwd
3. 统计分析(日志场景)
# 统计 access.log 中各 HTTP 状态码出现的次数
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 统计访问量最高的前 10 个 IP(awk 内建数组直接完成)
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head -10
# 计算某列总和:统计磁盘各分区使用量之和
df -m | awk 'NR>1 {sum += $3} END {print "总使用: " sum " MB"}'
# 找出响应大小超过 1MB 的请求
awk '$10 > 1048576 {print $7, $10}' access.log
4. BEGIN / END 块
# BEGIN 在处理前执行一次,END 在处理完执行一次 —— 适合做表头和汇总
awk -F: 'BEGIN {print "===== 用户列表 ====="} {print NR". "$1} END {print "共 " NR " 个用户"}' /etc/passwd
八、组合实战:一次完整的日志排查
假设线上接口报错,需要排查 Nginx 日志,典型排查链路如下:
# 1. 先看错误日志最近的 50 条记录
tail -50 /var/log/nginx/error.log
# 2. 实时跟踪日志(排障必备,Ctrl+C 退出)
tail -f /var/log/nginx/access.log
# 3. 定位今天 15 点左右的所有 500 错误,并保存到文件备查
grep "30/Sep/2026:15" access.log | grep '" 500 ' | tee 500_errors.txt
# 4. 统计这些错误是由哪些 IP 触发的,按次数排序
grep '" 500 ' access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
# 5. 错误请求和正常流量分开归档(重定向分离)
grep '" 500 ' access.log > 500.log
grep -v '" 500 ' access.log > normal.log
这套组合拳就是 Linux 日志处理的核心思路:grep 负责"找",sed 负责"改",awk 负责"算",重定向和管道负责"串联与落盘"。
九、本篇小结
| 三种数据流 | 0 标准输入、1 标准输出、2 标准错误,默认分别指向键盘和终端 |
| 输出重定向 | > 覆盖、>> 追加、2> 重定向错误、&> / 2>&1 合并输出 |
| 输入重定向 | < 从文件读入、<< 此处文档 |
| 管道与 tee | ` |
| grep | 按行过滤,-v/-n/-c/-i/-E 高频选项,配合正则精确匹配 |
| sed | 流式编辑,s/old/new/g 替换,d 删除,-i 落盘前先预览 |
| awk | 按列处理,-F 指定分隔符,$1 取列,支持数组统计与汇总 |
掌握这些命令后,面对任何日志文件都能快速完成"检索 → 过滤 → 统计 → 归档"的完整处理流程,这也是 SRE/运维日常排障的基本功。
网硕互联帮助中心





评论前必须登录!
注册