这个命令是干啥的
这三个命令都是看文件内容的,但场景完全不同:
- ·cat:适合小文件,一次性全部输出到终端
- ·less:适合大文件,分页浏览,支持搜索和导航
- ·tail:适合看文件末尾,特别是实时跟踪日志
刚学 Linux 的时候我只会 cat,看啥都用 cat。大日志文件直接刷屏,日志实时跟踪更是无从下手。后来分清了这三个命令的使用场景,效率提升了一大截。
基本用法(3分钟上手)
cat 基础
# 看文件内容
cat file.txt
# 合并多个文件
cat file1.txt file2.txt > merged.txt
# 显示行号
cat -n file.txt
# 显示不可见字符(制表符、换行符)
cat -A file.txt
-A 会把制表符显示成 ^I,行尾显示 $。排查配置文件的空格和制表符混用时特别好用。
less 基础
# 分页查看
less file.txt
# 翻页和搜索
# 按 j/k 或 上/下 箭头:逐行滚动
# 按 空格键:下一页
# 按 b:上一页
# 按 / 后输入关键词:搜索
# 按 n:下一个匹配
# 按 N:上一个匹配
# 按 g:跳转到文件开头
# 按 G:跳转到文件末尾
# 按 q:退出
tail 基础
# 查看文件末尾10行
tail file.txt
# 查看末尾50行
tail -n 50 file.txt
# 实时跟踪文件新增内容
tail -f app.log
tail -f 是我每天用最多的命令。查线上问题的时候,开个终端跑 tail -f error.log,触发操作后马上就能看到日志。
进阶骚操作
1. cat 的骚操作
清空文件而不删除文件本身:
# 用重定向清空文件
> file.txt
# 或者用 /dev/null 清空
cat /dev/null > file.txt
这两种方式都只清空文件内容,文件依然存在,权限属性不变。如果你有程序在写这个文件(比如 Nginx 日志),清空后程序可以继续往里面写。如果你 rm 再 touch,新文件 inode 变了,程序可能就懵了。
创建文件并写内容:
# 创建文件并写入内容,Ctrl+D 结束
cat > newfile.txt << EOF
这是内容
多行内容
EOF
合并文件时加分隔符:
# 在合并文件间插入文件名作为分隔
for f in *.txt; do
echo "===== $f ====="
cat "$f"
done > all_merged.txt
2. less 的骚操作
less 实时跟踪:
# less 模式下按 F:进入跟踪模式(类似 tail -f)
# Ctrl+C 退出跟踪模式回到 less 页面
less +F app.log
这个用法很多人不知道。我在排查线上问题时,先用 less 打开日志文件搜索关键错误,然后按 F 开始实时跟踪,看到更多错误后再按 Ctrl+C 切换回浏览模式,继续搜索。一个 less 搞定所有操作。
less 打开文件到指定行或位置:
# 打开文件直接到第100行
less +100 file.txt
# 打开文件直接到第一次出现 "ERROR" 的位置
less +/ERROR app.log
# 显示行号
less -N file.txt
less 标记书签:
# 在 less 中按 m 后跟一个字母(比如 a):标记当前位置
# 按 ' 后跟同一个字母:跳转回标记位置
看长配置文件时,用 m 标记几个关键位置,来回切换很方便。
less 查看压缩文件:
less app.log.gz # 直接看,不需要先 gunzip
less 内置了 zlib 支持,能直接查看 gzip 压缩的文本文件。这个我经常用,服务器上老的日志文件都压缩了,less 直接打开,不用解压。
3. tail 的骚操作
-f 和 -F 的区别:
# 跟踪文件,但如果文件被删除/重命名,就停了
tail -f app.log
# 跟踪文件,如果文件被删除/重命名,会尝试重新打开
tail -F app.log
tail -f 跟踪的是文件描述符(inode)。如果日志轮转脚本把 app.log 重命名成 app.log.1 然后创建新的 app.log,tail -f 还是盯着旧的 app.log.1 不放,新日志你看不到。tail -F 检测到文件被替换后会重新打开,我经历过好几次因为没用 -F 而漏看日志的尴尬。
同时跟踪多个文件:
# 同时跟踪多个日志文件
tail -F /var/log/nginx/access.log /var/log/nginx/error.log
# 输出会带上文件名标签
# ==> /var/log/nginx/access.log <==
# 192.168.1.1 - - [22/Jun/2024:10:00:00 +0800] ...
# ==> /var/log/nginx/error.log <==
# 2024/06/22 10:00:01 [error] ...
线上排查问题时我经常开一个终端窗口,同时跟踪多个相关服务的日志,一眼就能看到上下游的调用顺序。
从指定行开始看:
# 从第1000行开始看
tail -n +1000 largefile.log
+1000 表示从第 1000 行开始。这个参数配合 head 或者 less 可以分段查看大文件。
避坑指南
坑1:cat 看二进制文件
# 终端直接乱码,甚至卡死
cat binary_file.bin
二进制文件打到终端上,终端会尝试解析不可打印字符,可能触发转义序列,轻则乱码,重则终端卡死。如果确认是二进制文件,用 hexdump、od 或者 xxd。
坑2:cat 大文件刷屏
# 几个G的日志文件,直接刷到眼睛瞎
cat hugefile.log
终端滚动几十万行,查个东西得翻半天。大文件用 less,日志实时用 tail -f,cat 只适合看一眼小文件。
坑3:管道里用 cat 多此一举
# 新手写法
cat file.txt | grep "error" # cat 完全多余
# 正确写法
grep "error" file.txt
猫管道(useless use of cat)是新手常见问题。很多命令可以直接接受文件名参数,不需要 cat 再管道。
坑4:tail -f 不会自动退出
# 这个命令会一直卡住
tail -f app.log
tail -f 是阻塞的,按 Ctrl+C 退出。SSH 会话断了的话进程也会断。需要后台运行的话用 nohup 或者 tmux。另外 tail -f 在日志文件被删除后可能一直卡着不输出,用 -F 替代。
坑5:less 搜索大小写
less 默认搜索是大小写敏感的。less -i 可以忽略大小写,或者搜索时用 /error 然后按 -i 切换。
实战场景(重点!结合真实运维场景)
场景1:排查线上错误
应用突然报错了,快速排查流程:
#!/bin/bash
# 快速定位问题工具
LOG_DIR="/var/log/myapp"
echo "=== 1. 最近10分钟的错误 ==="
# 实时跟踪错误日志,匹配最近10分钟的ERROR
tail -n 5000 "${LOG_DIR}/app.log" | grep "ERROR" | tail -20
echo "=== 2. 错误出现频率 ==="
# 统计ERROR类型分布
grep "ERROR" "${LOG_DIR}/app.log" | awk '{print $NF}' | sort | uniq -c | sort -rn | head -5
echo "=== 3. 观看实时日志 ==="
echo "按 Ctrl+C 退出跟踪"
# 使用 -F 避免日志轮转后跟丢
tail -F "${LOG_DIR}/app.log"
我线上排查问题的标准流程:先用 grep 捞历史日志里的 ERROR,然后 tail -F 实时盯住新的错误输出。一个终端搞定排查。
场景2:分析 Nginx 访问日志
#!/bin/bash
# 实时监控 Nginx 访问情况
NGINX_LOG="/var/log/nginx/access.log"
echo "实时 IP 访问排行(10秒更新)"
# 每10秒统计一次最近的IP访问排行
while true; do
clear
echo "=== 当前访问最多的 IP(最近1000条) ==="
tail -1000 "$NGINX_LOG" | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== 当前请求最多的 URL(最近1000条) ==="
tail -1000 "$NGINX_LOG" | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
sleep 10
done
这个脚本帮我发现过几次异常的爬虫行为。某个 IP 突然冲到第一,查一下 User-Agent 就能判断是正常爬虫还是恶意扫描。
场景3:日志分析的黄金组合
#!/bin/bash
# 分析昨天全天的日志
LOG_FILE="/var/log/myapp/app.$(date -d 'yesterday' +%Y%m%d).log"
echo "=== 昨天日志统计 ==="
# 总行数
TOTAL=$(wc -l < "$LOG_FILE")
echo "总请求量:$TOTAL"
# ERROR 数量
ERRORS=$(grep -c "ERROR" "$LOG_FILE")
echo "ERROR 数量:$ERRORS"
echo "错误率:$(echo "scale=2; $ERRORS * 100 / $TOTAL" | bc)%"
# 最频繁的错误类型
echo "=== 前10个最常见错误 ==="
grep "ERROR" "$LOG_FILE" | sed 's/.*ERROR //' | sort | uniq -c | sort -rn | head -10
这条命令是我每天的例行检查。如果错误率超过 1%,我会进一步用 less 打开日志文件,搜索具体的时间段和相关上下文。
今日作业
线上有一个持续写入的日志文件 /var/log/myapp/app.log,需要用一条命令实现以下效果:
1. 实时跟踪日志(日志轮转后依然能跟住)
2. 只显示包含 "ERROR" 或 "WARN" 的行
3. 同时跟踪该目录下的 error.log
写出来这条命令。
(提示:组合 tail -F 和 grep,多文件用通配符或者多个文件参数。)