“Linux运维高频命令”经常被整理成一张长长的参数表,但真正到了服务器上,高频出现的并不是命令数量,而是几类反复执行的动作:确认当前目录、查看日志尾部、找到占用端口的进程、判断磁盘为什么满、修改权限后验证是否生效。命令只是这些动作的入口。只背参数,遇到问题仍然会停在“我知道有 grep,但不知道该 grep 哪个文件”这一步。
这篇文章面向刚接手 Linux 服务器、需要独立完成日常巡检和故障初查的读者,也适合后端开发补足排障基本功。内容按“文件与目录、文本处理、权限与进程、资源与网络、日志排查”这条主线组织,每个部分给出常用命令、参数含义、典型输出和容易踩的坑。读完后应该能完成一套最小闭环:登录机器后确认位置,找到目标文件,筛出关键日志,定位异常进程,判断资源瓶颈,并在动手修改前做好影响范围确认。
90 分钟是一个建议节奏,不是硬性限制。更合理的用法是把它当成五段练习:前 20 分钟熟悉文件和目录,中间 20 分钟练习文本筛选,再用 20 分钟理解权限和进程,随后 20 分钟看磁盘与网络,最后 10 分钟把命令串成一条排查链路。每段都要在真实终端里敲一遍,而不是只看示例。下面从命令分类开始,先把“遇到什么问题该找哪类命令”这件事理清楚。
1. 先建立命令分类框架,比死记参数更有效
1.1 运维命令围绕文件、文本、进程、资源四类对象展开
Linux 命令看起来零散,实际可以按操作对象分成四类。第一类是文件和目录,解决“在哪里、有什么、怎么复制和删除”;第二类是文本内容,解决“日志里有没有某个关键字、某列数据怎么统计”;第三类是权限和进程,解决“这个用户能不能操作、这个服务对应哪个进程”;第四类是资源和网络,解决“磁盘、内存、端口、连接是否正常”。分类之后,记忆负担会明显下降,因为每条命令都能挂到一个具体问题上。
下面这张表可以作为速查框架。表中列出的是日常巡检和初查阶段最常见的命令,不包含编译、容器编排等更垂直的工具。
| 类别 | 代表命令 | 典型问题 | 常用参数 |
|---|---|---|---|
| 文件与目录 | pwd、ls、cd、cp、mv、rm、find |
文件在哪、怎么批量找、怎么安全删 | -l、-h、-a、-type、-mtime |
| 文本处理 | cat、less、tail、grep、sed、awk |
日志怎么看、关键字怎么筛、列怎么统计 | -n、-f、-r、-i、-A、-B |
| 权限与进程 | id、chmod、chown、ps、top、kill |
为什么没权限、服务对应哪个 PID | -ef、-p、-15、-9 |
| 资源与网络 | df、du、free、uptime、ss、lsof |
磁盘满、内存紧、端口被占 | -h、-i、-lntp、-sh |
这张表不需要一次背完。更有效的做法是遇到问题时先判断对象:如果问题是“文件找不到”,就查第一行;如果是“日志里查不到关键字”,就查第二行;如果是“端口起不来”,就查第四行。判断对象比回忆参数更重要,因为参数可以通过 man 或 --help 现场查,方向错了就会在无关命令上浪费时间。
举一个常见场景:服务启动失败,日志里提示“Address already in use”。这时不要先去翻配置文件,而应该确认端口被谁占用。用 ss -lntp 找到监听该端口的进程,再用 ps -fp PID 确认进程身份,最后才决定是停掉旧进程还是改新服务的端口。这条链路横跨“资源与网络”和“权限与进程”两类命令,如果只记住 systemctl restart,就会反复重启却解决不了问题。
1.2 学习环境可以试错,生产环境要先确认影响范围
同样的命令,在学习环境和生产环境中的风险完全不同。学习环境通常是自己虚拟机或本地容器,rm -rf 删错了可以重建,chmod -R 777 改坏了可以重装,重点是建立手感。生产环境则要先确认主机、路径、用户、影响范围和回滚方式,再决定是否执行。很多事故不是因为命令不会用,而是因为命令敲在了错误的目录或错误的机器上。
建议在学习和测试环境里刻意练习三类动作:第一,执行删除前先用 ls 或 find -print 确认目标;第二,修改配置前先复制一份带时间戳的备份;第三,执行影响服务的操作前先记录当前状态,例如进程 PID、端口监听情况、文件权限。这些动作看起来慢,但能把误操作概率降下来。
可以用下面这个检查顺序约束自己:
bash复制# 1. 确认当前主机和用户
hostname
whoami
# 2. 确认当前目录和目标路径
pwd
ls -ld /data/app/current
# 3. 确认变量展开结果,避免空变量参与删除
TARGET="/data/app/logs"
printf 'target=%s\n' "$TARGET"
ls -la "$TARGET"
这段命令没有直接修改任何文件,作用是把“我在哪、我是谁、我要操作哪个路径”显式打印出来。生产环境中最危险的情况之一是变量为空,例如脚本里写了 rm -rf "$LOG_DIR"/*,而 LOG_DIR 因为配置未加载为空,命令就会展开成 rm -rf /*。先 printf 或 echo 确认变量值,是成本很低但很有效的防护。
注意:不要在生产环境直接复制网上的一行命令执行,尤其是包含
rm、chmod、chown、dd、mkfs的命令。先拆开看每个参数,确认路径和变量,再决定是否运行。
学习环境与生产环境的差异可以归纳为下表:
| 维度 | 学习环境 | 生产环境 |
|---|---|---|
| 删除文件 | 可直接练习,重建成本低 | 先备份、先打印目标、确认无进程占用 |
| 修改权限 | 可以反复试 | 先确认属主属组和最小权限,避免 -R 777 |
| 终止进程 | 可随意 kill -9 |
优先 kill -15,确认业务影响和重启策略 |
| 查看日志 | 文件小,可直接 cat |
大文件用 tail、less,避免一次性读入 |
| 执行脚本 | 可在任意目录运行 | 确认工作目录、环境变量、日志输出和回滚方案 |
这张表的核心不是让生产环境变得“不敢操作”,而是把操作分成“可逆”和“不可逆”两类。查看、统计、打印通常可逆;删除、覆盖、改权限、杀进程通常不可逆或恢复成本高。先判断可逆性,再决定操作力度。
1.3 命令的输入、输出和退出码要一起看
很多初学者只关注命令输出,忽略了标准错误和退出码。Linux 命令通常有三个通道:标准输入 stdin、标准输出 stdout、标准错误 stderr。正常结果走 stdout,报错信息走 stderr。管道 | 默认只把 stdout 传给下一个命令,stderr 仍然直接打印到终端。理解这一点,才能解释“为什么 grep 不到内容,但屏幕上又有报错”。
退出码用 $? 查看,0 表示成功,非 0 表示失败。脚本里经常用退出码做判断,但交互式排查时也值得看一眼。例如 grep 没匹配到内容时返回 1,这并不一定是命令出错,而是“没有找到”。如果把 grep 放在 set -e 的脚本里,没匹配到就会让脚本退出,这是常见坑。
bash复制grep -n "ERROR" /var/log/app/app.log
echo "exit_code=$?"
如果日志里没有 ERROR,输出为空,exit_code=1。这时要区分“命令执行失败”和“没有匹配结果”。再看一个重定向示例:
bash复制# 标准输出写入文件,标准错误仍然打印到终端
find /data -name "*.log" > /tmp/found.txt
# 标准输出和标准错误都写入同一个文件
find /data -name "*.log" > /tmp/found.txt 2>&1
2>&1 的含义是把文件描述符 2(stderr)重定向到文件描述符 1(stdout)当前指向的位置。排查命令时,如果只重定向 stdout,错误信息仍会出现在终端;如果错误信息也被吞掉,就会出现“文件是空的,也不知道为什么”的情况。建议在脚本里同时记录输出和退出码,便于事后复盘。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文件与目录:先用 pwd 和 ls 确认位置,再谈增删改查
2.1 pwd、ls、cd 解决“我在哪、这里有什么”
登录服务器后的第一个动作不应该是直接敲业务命令,而是确认当前位置。pwd 打印当前工作目录,ls 列出目录内容,cd 切换目录。三个命令都很简单,但组合起来能避免大量“文件明明存在却找不到”的问题。尤其是通过 sudo su 切换用户、通过脚本启动服务、通过 cron 执行任务时,工作目录可能和预期完全不同。
bash复制pwd
ls -lh
ls -la
ls -lt
ls -lh 以人类可读的单位显示文件大小,-a 显示隐藏文件,-t 按修改时间倒序排列,-l 使用长格式。长格式的每一列都有含义:第一列是权限和文件类型,第二列是硬链接数,第三列是属主,第四列是属组,第五列是大小,第六到第八列是时间,最后一列是文件名。看懂这些列,后面排查权限问题时就不需要反复查资料。
text复制-rw-r--r-- 1 app app 12K Jul 10 09:30 application.log
drwxr-xr-x 2 app app 4.0K Jul 10 09:20 conf
第一字符 - 表示普通文件,d 表示目录,l 表示符号链接。后面九位每三位一组,分别是属主、属组、其他用户的读 r、写 w、执行 x 权限。比如 rw-r--r-- 表示属主可读写,属组和其他用户只读。目录的执行权限 x 表示可以进入该目录,这一点经常被忽略:如果目录没有 x,即使文件有读权限,也无法访问目录下的文件。
cd 本身不复杂,但要注意 cd - 返回上一次目录,cd ~ 回到当前用户家目录,cd .. 回到上级目录。脚本里推荐使用绝对路径或先 cd 到脚本所在目录,避免依赖调用者的当前目录。一个常见写法是:
bash复制SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR" || exit 1
这段代码先取得脚本所在目录的绝对路径,再切换过去。|| exit 1 表示切换失败就退出,避免后续命令在错误目录下继续执行。对于涉及文件读写的脚本,这个细节很重要。
2.2 cp、mv、rm 的覆盖与误删风险
cp 复制、mv 移动或重命名、rm 删除,这三个命令的日常使用频率很高,风险也集中在“覆盖”和“误删”上。默认情况下,cp 和 mv 可能直接覆盖目标文件,rm 不会进入回收站。很多发行版给 root 以外的用户配置了 alias cp='cp -i'、alias mv='mv -i'、alias rm='rm -i',但脚本环境和 root 用户不一定生效,所以不能依赖别名。
bash复制# 复制目录并保留权限、时间戳、符号链接
cp -a /data/app/conf /data/backup/conf-20250710
# 交互式覆盖,目标存在时先询问
cp -i application.yml application.yml.bak
# 移动前先确认目标目录
ls -ld /data/archive
mv /data/app/old.log /data/archive/
cp -a 等价于 -dR --preserve=all,会递归复制并尽量保留属性,适合备份配置目录。cp -i 在覆盖前询问,适合手工操作;脚本里如果确定要覆盖,应该显式使用 \cp 或 command cp 绕过别名,并在命令前后打印日志。不要在同一行里既做删除又做复制,否则出错后很难判断是哪一步失败。
rm 的风险最高。下面几种写法要特别小心:
bash复制# 危险:变量为空时会展开成 rm -rf /*
rm -rf "$APP_DIR"/*
# 相对路径删除,依赖当前目录
rm -rf logs/*
# 使用通配符前没有确认匹配范围
rm -f /data/app/*.log
更稳妥的做法是先把删除目标打印出来,确认无误后再执行。find 的 -print 和 -delete 可以分两步走,下一节会展开。对于日志清理,也可以使用 truncate 清空文件而不是删除文件,避免正在写日志的进程持有已删除文件句柄,导致磁盘空间不释放。
bash复制# 清空日志内容但保留文件句柄
: > /data/app/logs/application.log
: 是 shell 内置的空命令,> file 会截断文件。相比 rm 后让进程继续写,truncate 或 : > 更适合处理正在被写入的日志文件。不过执行前要确认文件路径和权限,避免清空错误文件。
2.3 find 按时间、大小、类型筛选,先打印再删除
find 是文件排查中最重要的命令之一,能按名称、类型、时间、大小、权限、属主等条件组合筛选。初学者容易一上来就写 -delete,更安全的习惯是先 -print 看结果,再替换成删除动作。
bash复制# 找出 /data/logs 下 7 天前的普通日志文件
find /data/logs -type f -name "*.log" -mtime +7 -print
# 找出大于 100MB 的文件
find /data -type f -size +100M -print
# 找出最近 30 分钟内修改过的文件
find /data/app -type f -mmin -30 -print
-type f 限定普通文件,-name 按文件名匹配,-mtime +7 表示修改时间在 7 天以前,-mmin -30 表示 30 分钟以内。时间参数容易混淆:+7 是大于 7 天,-7 是小于 7 天,7 是正好第 7 天。实际排查时建议先用 -print 验证数量,再决定是否批量处理。
确认结果后,可以用 -delete 删除,或者用 -exec 调用其他命令:
bash复制# 确认无误后删除 7 天前的日志
find /data/logs -type f -name "*.log" -mtime +7 -delete
# 批量压缩 7 天前的日志
find /data/logs -type f -name "*.log" -mtime +7 -exec gzip {} \;
-exec ... \; 会对每个匹配文件执行一次命令,{} 代表文件名。如果匹配文件很多,-exec ... + 会批量传参,效率更高。对于包含空格或特殊字符的文件名,配合 -print0 和 xargs -0 更稳妥:
bash复制find /data/logs -type f -name "*.log" -print0 | xargs -0 gzip
-print0 用空字符分隔文件名,xargs -0 按空字符解析,能避免文件名中的空格被拆成多个参数。这个组合在批量处理用户上传文件、日志文件时很实用。
3. 文本查看与处理:大文件不要直接 cat
3.1 cat、less、tail 的选择取决于文件大小和是否跟踪
查看文件内容至少有三种常见方式:cat 一次性输出全部内容,less 分页浏览,tail 查看尾部。小配置文件用 cat 很方便,几百 MB 的日志用 cat 会把终端刷爆,还可能占用大量内存和 IO。判断标准很简单:文件小且只需要快速看全貌,用 cat;文件大或需要搜索、翻页,用 less;只看最后几行或持续跟踪新内容,用 tail。
bash复制# 查看小配置文件
cat /etc/nginx/nginx.conf
# 分页查看大日志,支持 / 搜索、G 跳到底部、q 退出
less /var/log/app/application.log
# 查看最后 200 行
tail -n 200 /var/log/app/application.log
# 持续跟踪新写入的内容
tail -f /var/log/app/application.log
less 打开后,/关键字 向下搜索,?关键字 向上搜索,n 跳到下一个匹配,N 跳到上一个,G 到文件末尾,g 到文件开头,q 退出。这些操作比反复 cat | grep 更高效,尤其是在不确定关键字出现在哪一段时。
tail -f 会持续等待新内容,适合观察正在运行的服务日志。如果日志发生轮转,旧文件被重命名,tail -f 可能继续盯着旧文件。此时可以用 tail -F,它会跟踪文件名并在轮转后重新打开。生产环境排查实时问题时,tail -F 通常比 tail -f 更合适。
bash复制tail -F /var/log/app/application.log
另一个常用命令是 wc -l,用于统计行数。日志文件太大时,先 wc -l 知道规模,再决定用 tail 还是分段处理。
bash复制wc -l /var/log/app/application.log
3.2 grep 的递归、排除、上下文和计数
grep 是文本筛选的核心命令。最基本的用法是 grep "关键字" 文件,但实际排查中经常需要递归目录、排除无关目录、显示上下文、统计次数。下面这些参数值得优先掌握。
| 参数 | 作用 | 典型场景 |
|---|---|---|
-n |
显示行号 | 定位日志行 |
-i |
忽略大小写 | 关键字大小写不确定 |
-r |
递归目录 | 在配置目录中找配置项 |
-l |
只输出文件名 | 找出哪些文件包含关键字 |
-c |
统计匹配行数 | 快速判断错误数量 |
-A n |
显示匹配行后 n 行 | 看异常堆栈 |
-B n |
显示匹配行前 n 行 | 看请求上下文 |
-C n |
显示前后各 n 行 | 综合查看上下文 |
--exclude-dir |
排除目录 | 跳过 .git、node_modules |
bash复制# 递归查找配置项,显示行号
grep -rn "max_connections" /etc/nginx
# 忽略大小写统计 ERROR 出现次数
grep -ic "error" /var/log/app/application.log
# 显示 ERROR 前后各 3 行
grep -n -C 3 "ERROR" /var/log/app/application.log
# 只列出包含关键字的文件名,排除日志归档目录
grep -rl --exclude-dir=archive "timeout" /data/app/conf
grep -r 在大型目录上可能很慢,先缩小路径范围,或者用 --include 限定文件类型:
bash复制grep -rn --include="*.yml" "timeout" /data/app/conf
如果关键字包含正则特殊字符,使用 -F 按固定字符串匹配,避免转义问题:
bash复制grep -F "http://example.com/api?x=1" access.log
grep 的退出码也要注意:匹配到返回 0,没匹配到返回 1,命令本身出错返回 2。脚本里判断“日志中是否存在 ERROR”时,不要只判断输出是否为空,最好结合退出码。
3.3 sed 替换与 awk 按列统计
sed 适合做流式替换和按行筛选,awk 适合按列处理。两者经常和管道一起使用。先看 sed 的常见写法:
bash复制# 将文件中的 old 替换为 new,先输出到终端确认
sed 's/old/new/g' application.yml
# 确认无误后原地修改,并保留备份
sed -i.bak 's/old/new/g' application.yml
# 打印第 10 到 20 行
sed -n '10,20p' application.log
# 删除空行
sed '/^$/d' application.log
sed -i 直接修改文件,风险在于替换范围可能超出预期。建议先不加 -i 运行一次,确认输出正确后再加 -i.bak 保留备份。s/old/new/g 中的 g 表示一行内替换所有匹配,不加 g 只替换每行第一个。如果替换内容包含 /,可以换分隔符,例如 s#/data/app#/opt/app#g,避免大量转义。
awk 的典型用法是按列取值、过滤和统计。默认以空白字符分隔,$1 是第一列,$2 是第二列,$0 是整行,NF 是列数,NR 是行号。
bash复制# 打印访问日志的第一列和第七列
awk '{print $1, $7}' access.log
# 筛选状态码为 500 的行
awk '$9 == 500 {print $0}' access.log
# 统计每个 IP 的访问次数,取前 10
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -n 10
最后一条命令是日志分析中的经典组合:awk 取第一列 IP,sort 排序,uniq -c 统计连续重复次数,sort -nr 按数字倒序,head 取前 10。理解每个管道的作用,比记住整行命令更重要。uniq -c 只能统计相邻重复行,所以前面必须先 sort,否则同一个 IP 可能被统计成多段。
如果日志字段分隔符不是空白,可以用 -F 指定:
bash复制awk -F',' '{print $1, $3}' app.csv
awk 还可以做简单求和,例如统计响应时间总和:
bash复制awk '{sum += $10} END {print sum}' access.log
这类命令适合快速估算,不适合替代完整的日志分析系统。生产环境如果要做长期统计,应该把日志接入集中式平台,而不是每次登录机器临时 awk。
4. 权限、用户与进程:先确认身份,再决定能不能动
4.1 用 id、ls -l、stat 确认权限和属主
Linux 的权限问题经常表现为“文件明明存在,但就是打不开”。这时先确认当前用户身份和目标文件权限。id 显示当前用户的 UID、GID 和所属组,whoami 只显示用户名,ls -l 看文件权限,stat 看更详细的时间、inode 和权限信息。
bash复制id
whoami
ls -l /data/app/application.yml
stat /data/app/application.yml
如果当前用户是 app,文件属主是 root,权限是 600,那么 app 用户无法读取。解决方式不是直接 chmod 777,而是判断应该改属主还是改权限。例如配置文件归 app 用户读取,可以 chown app:app application.yml 并设置为 640;可执行脚本可以设置为 750。最小权限原则比“全开”更安全。
bash复制# 修改属主和属组
chown app:app /data/app/application.yml
# 属主读写,属组只读,其他用户无权限
chmod 640 /data/app/application.yml
# 属主读写执行,属组读执行,其他用户无权限
chmod 750 /data/app/start.sh
chmod 的数字表示法里,r=4、w=2、x=1,三组分别对应属主、属组、其他用户。640 就是 rw- r-- ---。符号表示法更适合增量修改,例如 chmod u+x start.sh 给属主增加执行权限,chmod g-w file 去掉属组写权限。
注意:
chmod -R 777会让所有用户对目录和文件拥有读、写、执行权限,短期内可能“解决问题”,但会扩大安全风险。应先确认报错的具体文件、具体用户和具体操作,再给最小权限。
4.2 ps、top、pgrep 定位进程,kill 要分级
进程排查的第一步是找到 PID。ps -ef 列出所有进程,ps -fp PID 查看指定进程,pgrep -af 关键字 按名称或命令行匹配。top 适合实时观察 CPU 和内存占用,top -p PID 只看指定进程。
bash复制# 查找 Java 进程
ps -ef | grep java
# 更精确地按命令行匹配,避免 grep 自身干扰
pgrep -af "application.jar"
# 查看指定 PID 的详细信息
ps -fp 12345
# 实时观察指定进程
top -p 12345
ps -ef | grep java 的输出里经常包含 grep java 自身,这是初学者常见的疑惑。使用 pgrep -af 或 ps -ef | grep "[j]ava" 可以避免。找到 PID 后,终止进程要分级:先 kill -15 PID 发送 SIGTERM,让进程有机会执行关闭钩子、释放资源、完成日志刷盘;等待几秒后如果还没退出,再考虑 kill -9 PID。
bash复制kill -15 12345
sleep 5
ps -fp 12345
kill -9 是 SIGKILL,进程无法捕获,也不会执行清理逻辑。数据库、消息队列、正在写文件的进程被 -9 终止后,可能留下未完成的事务或临时文件。生产环境应优先使用服务管理工具的重启命令,例如 systemctl restart app,让系统按配置的停止方式处理。
4.3 端口占用用 ss 和 lsof 交叉确认
服务启动报“端口被占用”时,需要找到监听该端口的进程。ss -lntp 列出 TCP 监听端口和对应进程,lsof -i:8080 也可以按端口查进程。两者可以交叉验证,避免只看一个命令的输出。
bash复制# 查看所有 TCP 监听端口和进程
ss -lntp
# 查看 8080 端口占用
ss -lntp | grep ':8080'
# 使用 lsof 查询
lsof -i:8080
ss 输出中的 Local Address:Port 是本地监听地址,Process 列显示进程名和 PID。如果 Process 列显示 -,通常是因为当前用户权限不足,看不到其他用户的进程信息,可以用 sudo 再执行。lsof 的输出包含 COMMAND、PID、USER、FD、TYPE、DEVICE、SIZE、NODE、NAME,其中 NAME 列会显示协议和端口。
确认 PID 后,不要立即 kill -9。先 ps -fp PID 看进程属于哪个服务、启动命令是什么、运行了多久,再决定是停旧服务还是改新服务端口。如果占用端口的是自己刚启动的测试进程,可以正常停止;如果是生产服务,需要确认重启策略和依赖关系。
5. 磁盘、网络与资源:先看整体,再看细节
5.1 df 和 du 结果不一致的常见原因
磁盘满是最常见的线上问题之一。df -h 看文件系统整体使用率,du -sh 看目录占用大小。两者结果不一致时,不要急着怀疑命令有问题,先检查几种常见原因。
bash复制# 查看文件系统使用率和 inode 使用率
df -h
df -i
# 查看当前目录下各子目录大小
du -h --max-depth=1 . | sort -hr
# 查看指定目录总大小
du -sh /data/logs
df 统计的是文件系统块使用情况,du 统计的是目录下文件占用的块数。常见差异原因包括:文件被删除但仍被进程持有句柄,du 看不到但 df 仍占用;目录跨越了不同挂载点;存在稀疏文件;df 看到的是整个文件系统,而 du 只统计指定目录。还有一种容易忽略的情况是 inode 耗尽:df -h 显示空间充足,但 df -i 显示 inode 使用率 100%,此时无法创建新文件。
| 现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
df 显示满,du 加起来不大 |
已删除文件仍被进程占用 | lsof +L1 查找 deleted 文件 |
重启或通知进程重新打开日志 |
df -h 有空间但无法写文件 |
inode 耗尽 | df -i 查看 IUse% |
清理小文件、临时文件、缓存 |
du 统计很慢 |
目录文件数量巨大 | du 加 --max-depth 缩小范围 |
分目录统计,避免全盘扫描 |
| 目录大小与预期不符 | 挂载点覆盖了原目录 | df -h 看挂载点 |
检查挂载顺序和目录内容 |
lsof +L1 用于查找 link count 小于 1 的已删除文件。如果看到某个日志文件被进程持有但已删除,可以通过重启进程或让进程重新打开日志文件来释放空间。直接删除文件不一定能立刻释放空间,这是磁盘排查中的经典坑。
5.2 ss 查看监听与连接状态
ss 是查看网络连接的高频命令,逐渐替代了 netstat。常用参数组合是 -lntp:-l 只看监听,-n 不解析服务名,-t 只看 TCP,-p 显示进程。查看所有连接可以用 ss -ant,按状态过滤可以用 state。
bash复制# 查看 TCP 监听端口
ss -lntp
# 查看所有 TCP 连接
ss -ant
# 查看已建立连接的统计
ss -ant state established | wc -l
# 查看 3306 端口的连接
ss -ant '( sport = :3306 )'
ss 输出中的 Recv-Q 和 Send-Q 表示接收队列和发送队列。如果 Recv-Q 持续很高,可能说明应用处理不过来;如果 Send-Q 很高,可能说明网络发送受阻。State 列常见状态包括 LISTEN、ESTAB、TIME-WAIT、CLOSE-WAIT。大量 CLOSE-WAIT 通常和应用没有正确关闭连接有关,需要检查代码中的连接释放逻辑。
5.3 free、uptime、vmstat 判断负载和内存
free -h 查看内存,uptime 查看系统负载,vmstat 查看 CPU、内存、IO 的综合情况。三者组合可以快速判断机器是否资源紧张。
bash复制free -h
uptime
vmstat 1 5
free -h 输出中的 free 是完全空闲内存,available 是估算的可用内存。Linux 会用空闲内存做缓存,所以 free 很小并不一定代表内存不足,重点看 available 和 swap 使用情况。如果 available 很低且 swap 使用持续增长,说明内存压力较大。
uptime 输出的 load average 是 1 分钟、5 分钟、15 分钟的平均负载。负载高低要结合 CPU 核数看:4 核机器负载 4 左右接近满载,8 核机器负载 4 还有余量。vmstat 1 5 每秒采样一次,共 5 次,重点看 r(运行队列)、si/so(换入换出)、us/sy/wa(用户态、内核态、IO 等待)。如果 wa 很高,说明 IO 可能是瓶颈;如果 r 持续大于 CPU 核数,说明进程在排队。
6. 日志排查链路:把 grep、tail、awk 串起来
6.1 从关键字到时间窗口的筛选
日志排查通常从一个关键字开始,例如“ERROR”“Timeout”“Connection refused”。先确定日志文件,再用 tail 看尾部,用 grep 筛关键字,用 awk 或 sed 缩小时间范围。不要一上来就对整个大文件 grep,先看文件大小和最后修改时间。
bash复制# 确认日志文件大小和修改时间
ls -lh /var/log/app/application.log
# 查看最后 500 行中的 ERROR
tail -n 500 /var/log/app/application.log | grep -n "ERROR"
# 统计最近 1000 行中不同错误码的数量
tail -n 1000 /var/log/app/application.log | grep -o "code=[0-9]*" | sort | uniq -c | sort -nr
如果日志格式包含时间,可以用 awk 按时间范围过滤。假设每行开头是 2025-07-10 09:30:00 这样的格式:
bash复制awk '$1" "$2 >= "2025-07-10 09:00:00" && $1" "$2 <= "2025-07-10 10:00:00"' application.log
这种写法适合格式固定的日志。如果时间格式不固定,使用 sed -n '/开始时间/,/结束时间/p' 按行区间截取更直接:
bash复制sed -n '/2025-07-10 09:00:00/,/2025-07-10 10:00:00/p' application.log
sed 区间会从第一个匹配行打印到第二个匹配行,适合日志中时间戳按顺序出现的情况。如果时间跨度大、文件大,还是应该使用日志平台或 awk 做精确过滤。
6.2 用管道和 xargs 批量处理文件
管道把前一个命令的 stdout 交给后一个命令,xargs 把标准输入转换成命令行参数。两者组合可以批量处理文件,但要注意文件名中的空格和特殊字符。前面提到的 find -print0 | xargs -0 是最稳妥的写法。
bash复制# 找出包含 OutOfMemory 的日志文件
grep -rl "OutOfMemory" /data/logs
# 批量统计这些文件的行数
grep -rl "OutOfMemory" /data/logs | xargs wc -l
# 文件名可能包含空格时使用 -print0
find /data/logs -type f -name "*.log" -print0 | xargs -0 grep -l "OutOfMemory"
xargs 默认按空白分隔参数,如果文件名包含空格,会被拆成多个参数。-0 配合 find -print0 可以解决这个问题。批量替换时也要谨慎,例如:
bash复制find /data/app/conf -type f -name "*.yml" -print0 | xargs -0 sed -i.bak 's/127.0.0.1/localhost/g'
这条命令会修改所有匹配的 YAML 文件,并生成 .bak 备份。执行前建议先去掉 -i.bak,只打印替换结果,确认范围正确后再批量修改。
6.3 把常用排查命令固化成脚本或别名
反复敲长命令容易出错,可以把稳定的排查链路写成脚本或别名。脚本里要加注释、打印关键信息、检查命令是否存在,并避免硬编码路径。下面是一个简单的巡检脚本示例,只做信息收集,不修改任何文件。
bash复制#!/usr/bin/env bash
set -u
LOG_FILE="/var/log/app/application.log"
APP_PORT="8080"
echo "===== host ====="
hostname
date
echo "===== disk ====="
df -h
echo "===== inode ====="
df -i
echo "===== memory ====="
free -h
echo "===== port ${APP_PORT} ====="
ss -lntp | grep ":${APP_PORT}" || echo "port ${APP_PORT} not listening"
echo "===== recent error ====="
if [ -f "$LOG_FILE" ]; then
tail -n 200 "$LOG_FILE" | grep -i "error" || echo "no error in last 200 lines"
else
echo "log file not found: $LOG_FILE"
fi
这个脚本只读不写,适合作为日常巡检的起点。set -u 让未定义变量报错,避免变量为空导致意外路径。生产环境可以把输出重定向到带日期的文件,或者接入监控系统。不要在没有确认的情况下把清理、重启、删除动作放进巡检脚本,巡检和变更应该分开。
注意:别名只对当前 shell 生效,脚本中不一定加载。不要依赖
alias rm='rm -i'保护脚本里的删除操作,脚本应该显式检查路径和变量。
7. 常见坑、检查清单与 90 分钟练习路径
7.1 六个高频误操作和规避方式
下面这些坑在初学阶段出现频率很高,每一个都和本文主题直接相关。
| 错误写法或现象 | 为什么会错 | 推荐做法 |
|---|---|---|
rm -rf $DIR/* 变量为空 |
展开成 rm -rf /* |
先 printf 确认变量,使用 set -u |
sed -i 直接改配置 |
替换范围超出预期,无法回滚 | 先不加 -i 预览,再加 -i.bak |
服务起不来就 kill -9 |
跳过清理逻辑,可能丢数据 | 先 kill -15,确认进程状态 |
大日志直接 cat |
终端刷屏、IO 高、难定位 | 用 less、tail、grep 缩小范围 |
chmod -R 777 解决权限 |
扩大安全风险,掩盖真实问题 | 确认用户和文件,给最小权限 |
只看 df -h 不看 inode |
空间够但无法创建文件 | 同时执行 df -i |
以 kill -9 为例,很多教程说“杀不掉就 -9”,但省略了前置条件。正确顺序是:先确认 PID 对应的是不是目标进程,再尝试 kill -15,等待一段时间,观察进程是否退出、日志是否刷盘、端口是否释放。只有进程无响应且确认可以强制终止时,才使用 kill -9。数据库、消息队列、文件写入类服务尤其要谨慎。
再以 chmod -R 777 为例,报错 Permission denied 时,先看是哪个用户、哪个文件、哪个操作。可能是目录缺少 x 权限,也可能是文件属主不对,还可能是 SELinux 或挂载选项限制。直接 777 只是把问题暂时压下去,后续可能带来安全审计问题。推荐用 namei -l /path/to/file 查看路径上每一级目录的权限,逐级定位。
7.2 生产环境执行命令前的检查清单
下面这份清单适合放在运维手册或脚本注释里。每次执行可能影响服务的命令前,按顺序确认。
- 确认主机名和 IP,避免在错误机器上操作。
- 确认当前用户和权限,区分普通用户与
root。 - 确认目标路径是绝对路径,变量已展开且非空。
- 确认操作是否可逆,删除、覆盖、改权限前先备份。
- 确认是否有进程正在使用目标文件,必要时用
lsof检查。 - 确认影响范围,是否只影响单机、单服务还是整个集群。
- 确认维护窗口和回滚方案,重要变更提前通知相关人。
- 确认日志输出位置,命令执行后能查到结果和错误。
- 确认监控指标,变更后观察端口、错误率、延迟和资源使用。
- 确认退出码和关键输出,不要只看“没有报错”就认为成功。
这份清单不需要每次全写出来,但关键步骤不能省。尤其是第 3 条和第 4 条,能挡住大部分误删和误覆盖。
7.3 90 分钟练习路径和命令组合
最后给一条可执行的练习路径。建议在本地虚拟机或测试容器中进行,不要直接在生产环境练习删除和权限修改。
第一阶段,20 分钟,文件与目录:
bash复制mkdir -p /tmp/linux-drill/logs
cd /tmp/linux-drill
touch logs/app.log logs/error.log
ls -lah
find . -type f -name "*.log" -print
第二阶段,20 分钟,文本处理:
bash复制printf 'INFO start\nERROR timeout\nINFO retry\nERROR failed\n' > logs/app.log
grep -n "ERROR" logs/app.log
grep -c "ERROR" logs/app.log
awk '{print $1}' logs/app.log | sort | uniq -c
sed -n '2,3p' logs/app.log
第三阶段,20 分钟,权限与进程:
bash复制id
chmod 640 logs/app.log
ls -l logs/app.log
sleep 300 &
pgrep -af "sleep 300"
kill -15 <PID>
第四阶段,20 分钟,资源与网络:
bash复制df -h
df -i
free -h
uptime
ss -lntp
第五阶段,10 分钟,综合排查。假设服务端口是 8080,按“端口是否监听、进程是否存在、日志是否有错误、磁盘是否充足”的顺序走一遍:
bash复制ss -lntp | grep ':8080' || echo "port not listening"
pgrep -af "application.jar" || echo "process not found"
tail -n 100 /var/log/app/application.log | grep -i "error" || echo "no recent error"
df -h
练习时不要只追求命令执行成功,要观察输出中的每一列,理解命令为什么这样组合。比如 ss -lntp 的输出里,LISTEN 表示监听状态,0.0.0.0:8080 表示监听所有网卡,127.0.0.1:8080 表示只监听本机。绑定地址不同,外部能否访问也不同,这类细节只有实际看过输出才会形成判断。
真正进入生产环境后,建议把本文中的只读排查命令先固化成自己的习惯,再逐步加入变更类命令。先能准确描述“现在发生了什么”,再决定“要改什么”。Linux 命令的数量很多,但日常运维高频动作相对稳定,围绕文件、文本、权限、进程、资源、网络这六个对象反复练习,比背完整本命令手册更实用。
