Appearance
管道与重定向
Shell 里处理日志、串联命令和写脚本时,离不开标准输入输出、重定向和管道。
一、三个标准通道
每个进程启动时,内核默认打开三个文件描述符。
| 文件描述符 | 名称 | 默认指向 | 用途 |
|---|---|---|---|
0 | stdin | 键盘 | 命令读取数据的地方 |
1 | stdout | 终端屏幕 | 命令正常输出结果的地方 |
2 | stderr | 终端屏幕 | 命令输出错误信息的地方 |
在 Shell 里,正常输出和错误输出默认都打到屏幕上,看起来混在一起。但底层是分开的两条通道,可以分别重定向。
bash
command >out.log 2>err.log> 默认只重定向 stdout(等同于 1>)。stderr 不会被它带走,仍然会打印到屏幕上。
二、重定向
覆盖写入:
bash
echo "hello" > file.txt追加写入(保留原有内容):
bash
echo "world" >> file.txt单独重定向错误输出:
bash
ls /not-exist 2> err.log将 stdout 和 stderr 合并到同一个文件:
bash
command > all.log 2>&1
command &> all.log # 上面一句的简写形式2>&1 的写法有顺序要求。command >all.log 2>&1 的执行顺序是:先 >all.log(把 stdout 指到文件),再 2>&1(把 stderr 指向当前的 stdout,即那个文件)。如果反过来写 2>&1 >all.log,stderr 先指向原来那个终端 stdout,然后 stdout 才被改掉——结果是 stderr 还打印在屏幕上。
定时任务里常见的写法:
bash
*/5 * * * * /opt/check.sh >>/var/log/check.log 2>&1用 >> 追加而不覆盖。脚本执行频繁时,还要配合 logrotate 控制日志文件大小。
三、丢弃输出
bash
command >/dev/null
command >/dev/null 2>&1/dev/null 是一个特殊的设备文件,写进去的数据会被直接丢弃。临时调试时可以用,生产脚本里把 stdout 和 stderr 全部丢弃后,失败时没有任何排查线索。至少保留 stderr 或关键步骤的输出。
四、管道
管道 | 把前一个命令的 stdout 接到后一个命令的 stdin。
bash
ps aux | grep nginx管道只传送 stdout,不传送 stderr。前一个命令报错时,错误信息仍然会直接显示在终端上。
很多命令可以直接读取文件,不需要 cat 中转:
bash
grep " 500 " access.log # 更直接
cat access.log | grep " 500 " # 多了一个进程,但功能一样小文件无所谓,脚本里大批量处理时少一次管道就少一次进程开销。
管道的一个容易忽略的问题:默认情况下,管道命令的退出码是最后一个命令的退出码。
bash
grep "ERROR" app.log | head如果 head 成功但 grep 没找到匹配而返回失败(grep 没匹配到返回 1),整体退出码是 0。巡检脚本里写上:
bash
set -o pipefailpipefail 打开后,管道中任何一个命令失败,整条管道的退出码就是失败。
五、tee
tee 像 T 型三通——数据流进来,一份写到文件,一份继续输出到终端(或传给下一个命令)。
bash
echo "hello" | tee file.txt
echo "world" | tee -a file.txt # -a 追加而非覆盖一个常见场景:需要 sudo 权限写入受保护的文件:
bash
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ip-forward.conf这里用 tee 而不是 sudo echo ... > file 的原因:重定向 > 发生在当前 Shell 进程里,不具备 sudo 权限;而 tee 以 sudo 身份运行,有权限打开目标文件。
六、xargs
xargs 把标准输入转换成命令的参数。
bash
find /var/log -name "*.log" | xargs ls -lhxargs 从 stdin 读取数据,按空白字符分割后,拼到目标命令的末尾执行。默认的分割逻辑遇到文件名中有空格时会出错,配合 -print0 和 -0 使用 null 字符作为分隔符:
bash
find /var/log -name "*.log" -print0 | xargs -0 ls -lh删除文件前先打印确认:
bash
find /tmp -type f -mtime +7 -print # 先看哪些会被删除
find /tmp -type f -mtime +7 -print0 | xargs -0 rm -f # 确认后再删在执行批量删除之前先跑一遍预览,避免路径条件写错导致误删。这是 find + xargs 组合操作的安全习惯。
七、几个常用组合
统计访问来源 IP 的 TOP N:
bash
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head每条命令各司其职:awk 提取第一列(IP),sort 排序让相同 IP 聚在一起,uniq -c 统计每个 IP 出现次数,sort -nr 按次数降序排列,head 取前几行。
查看监听端口和对应进程:
bash
ss -lntp | grep ':80'过滤最近错误日志的最后 50 行:
bash
grep -i "error" app.log | tail -n 50统计 500 状态码的请求数:
bash
grep ' 500 ' access.log | wc -l管道串到三四段以上时,可读性和排错都会变差。写成脚本、给中间结果起变量名,下次回头看时容易理解在做什么。