写这一篇的时候,距离这个系列第一篇已经隔了不少时间。前面九篇把文件操作、权限、压缩、文本处理这些基础命令都过了一遍,如果你全部跟下来,单条命令大概率已经不成问题。但有个现象我反复遇到:新人拿着命令清单,看到一台陌生的服务器或者接到一个故障通知,依然会卡住手脚——因为真实环境里没有哪道题会写着“这道题用awk解决”,所有问题都是拿到现场信息,再临时决定用哪条命令。所以作为系列第十篇,我想换一种方式来写:不按命令类型往下罗列,而是用四个最常见的排障和运维场景来带命令,后面再补两块日常高频的组合操作。这样既能复习前几篇里出现过的基础命令,又能补上一些偏实战的系统级命令,争取让你看完就能直接照着用。
1. 拿到陌生系统先别慌:系统信息聚合命令,摸清家底再动手
我刚工作那会儿,接手一台没人维护的服务器,第一件事就是连上去看目录都是乱的,想干什么都不知道从哪下手。后来带我的师傅说了一句话,我一直记着:排查问题前先分清是“资源不够”还是“程序写错”,这两个方向差了十万八千里。分清的第一步,就是用系统信息命令把机器的家底摸清楚。
1.1 lscpu与free:CPU和内存的真实与表面
lscpu 是查看CPU信息最快的方式,不用去 /proc/cpuinfo 里翻半天。它的输出直接列清楚了架构、型号、核心数、线程数、套接字数,还有CPU的MHz频率和虚拟化支持情况。比如我遇到过一台机器,业务方说是单核,但lscpu一看是4核8线程,程序是按单核写的,后来一查问题根本不在资源上,而在锁竞争。这种误判,就是没先看硬件信息。
重点看这几项:
- Architecture:x86_64还是aarch64,决定你装软件包选哪个版本。
- CPU(s):逻辑核总数,按这个来定线程池大小、并发数。
- Core(s) per socket:每个物理CPU的物理核数。
- Model name:具体型号,排查指令集兼容性时有用。
内存这边,很多人习惯只看 free -h 的used列,这其实是个大坑。Linux的内存策略决定了它会尽量用内存做页缓存,所以 free 列出来的“free”往往很小,但这是正常的,不代表内存不够。真正要看的字段是 available,它表示“在不触发明显交换的情况下,还能分给新进程多少内存”。我接手过一台报OOM的机器,业务同事截图说used占了95%,我上去free -h一看,available还剩8G多,问题根本不在这台机器上,而是被监控脚本误判了。
bash复制free -h
建议养成习惯:内存告警先看available,再看si/so(swap换入换出量)。如果si和so两个数字长期不为0,说明物理内存确实顶不住了,这时候再怀疑内存不足才合理。
1.2 lsblk、df、blkid:磁盘关系盘清楚
磁盘相关的命令我建议按三层来看:lsblk 看结构,df 看使用率,blkid 看磁盘身份标识。三者的分工完全不同,但组合起来能把一台机器的磁盘情况看得很透彻。
lsblk 以树状结构展示块设备,挂载点、容量、类型一眼就能看明白。新加一块磁盘时,用 lsblk 看到一块没有挂载点的新盘,是最快的确认方式。df -hT 则是从文件系统角度统计使用率,其中 -T 会显示文件系统类型,这一点在判断是ext4、xfs还是overlay时特别有用。比如容器环境里常见overlay,你用df只看容量没问题,但要知道这是堆叠文件系统,实际底层空间可能来自同一个宿主机分区,这时候单独df的容量数字就有误导性。
bash复制# 查看块设备树状结构
lsblk
# 查看文件系统容量和使用率,带文件系统类型
df -hT
# 查看指定磁盘的UUID和文件系统类型
blkid /dev/sda1
还有一个容易踩的坑是inode耗尽。df -hT 看容量明明还有剩余,但系统就是报“No space left on device”,这种情况八成是inode被小文件占满了。用 df -i 看一下就明白,inode使用率达到100%时,文件系统写不进去任何新文件,哪怕容量还很多。常见于邮件队列目录、临时目录或者跑定时任务的输出目录。
1.3 vmstat与lspci:瞬时状态与外设识别
前面几个命令看的是静态信息,但排查性能问题的时候还需要动态观察。vmstat 1 5 是一个经典的动态采样命令,后面跟两个参数,单位是秒,表示每隔1秒输出一次,共输出5次。采样输出里最值得关注的是 r 和 wa 两列:r表示当前等待CPU的进程队列长度,持续大于CPU核数,说明CPU存在竞争;wa表示I/O等待时间占比,如果这个值长期很高,说明磁盘或存储子系统是瓶颈。
bash复制vmstat 1 5
外设识别方面,lspci 可以列出所有PCI总线上的设备,网卡、显卡、RAID卡都能看到。排查网卡丢包、型号不识别这类问题时,先跑一条 lspci | grep -i ethernet,再对应去看驱动模块是否加载,路径清晰很多。像我们排查过一台服务器的网卡流量异常,最后发现是机器上有两套网卡,业务流量走的是板载网卡,监控看的却是独立网卡,两个lspci一对比就清楚了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 查日志是排障的第一步:journalctl和dmesg的高效检索姿势
很多人排障第一反应是去 /var/log 下面翻文件,这个方向没错,但效率太低。现代主流发行版基本都用systemd管理服务,日志体系也统一由journald接管。与其到每个服务的日志目录里人肉grep,不如先把journalctl用熟。
2.1 journalctl的实用过滤组合
journalctl 的基本用法很简单,直接敲就是全部日志,但没人会真去翻全量日志。实际使用通常要组合过滤条件,我最常用的是四件套:-u 指定服务单元,--since/--until 指定时间范围,-p 指定优先级,-b 指定启动周期。
比如nginx启动失败,我想看最近10分钟内nginx服务相关的错误信息,一条命令就能完成:
bash复制journalctl -u nginx --since "10 minutes ago" -p warning --no-pager
这里的 -p warning 会过滤掉info、debug这类噪音,让问题点更明显。--no-pager 是防止输出太长时进入交互式翻页,脚本和远程执行时很方便,不加的话在自动化任务里容易卡住整个流程。
还有一个我后来才学会的用法:journalctl -x 会让systemd自动补充日志的详细解释。很多内核报错或者服务状态信息,光看缩写的日志正文根本不知道什么意思,加上 -x 后systemd会尝试把日志中带ID的条目解析成可读的文字说明,对新人特别友好。
bash复制# 查看本次启动以来的错误日志
journalctl -b -p err
# 查看指定服务的最近50条日志
journalctl -u nginx -n 50
踩过的一个小坑:有些服务没有通过systemd启动,而是直接在shell里跑的,这时候 journalctl -u 什么都查不到。遇到这种情况,要么去 /var/log 下的传统日志文件找,要么用 ps 找到PID后去 /proc/PID/fd/1 看它的标准输出重定向文件,这才是真实的日志流位置。
2.2 dmesg里的内核线索:OOM、硬件错误、断连
如果说journalctl主要是用户态服务的日志,那 dmesg 看的就是内核日志环缓冲区,很多硬件层面和应用层根本感知不到的问题,都要来这里找证据。比如经典的“数据库进程莫名消失”,杀掉进程的往往是内核OOM killer,而不是应用自己退出。这种情况在应用日志里通常查不到异常,因为连进程本身都是被强杀的,应用根本来不及记录。
bash复制# 显示内核日志,并转为人类可读的时间戳
dmesg -T
# 只看emerg到err级别的内核日志
dmesg -T -l emerg..err
# 直接grep OOM相关的杀进程记录
dmesg -T | grep -i "killed process"
这里有个特别容易坑人的细节:dmesg 默认显示的时间戳是从开机到当前时间的相对秒数,不是绝对时间。我第一次排障时看到一条OOM记录,时间戳是“1225.098”,以为没什么参考意义,后来才意识到这是开机后第1225秒发生的——换算一下,再对照业务报障时间,完全对得上。所以在生产环境一定要用 dmesg -T,把相对时间转换成绝对时间,否则时间线分析根本没法做。
另外像TCP丢包、网卡链路物理断开、磁盘I/O错误这类问题,dmesg里往往会在故障发生瞬间留下一条内核级别的告警。养成一个习惯:应用日志查不出问题的时候,去dmesg里转一圈,往往有惊喜。
2.3 日志文件与journald的磁盘占用管理
journald日志有个老毛病:默认它会吃掉不少磁盘空间。长时间不清理的机器,/var/log/journal 目录能占到好几个G,甚至直接把根分区打满。根分区打满的后果非常严重,sshd、cron这些基础服务都会开始报错,整个系统处于一种“活着但没完全活着”的状态。
查看日志占用和清理,用这两条命令:
bash复制# 查看journal日志当前占用磁盘大小
journalctl --disk-usage
# 清理日志,使总占用降到200MB以下
journalctl --vacuum-size=200M
更彻底的办法是修改配置,在 /etc/systemd/journald.conf 里找到 SystemMaxUse 这一项,咱们给它设置一个上线,比如:
conf复制SystemMaxUse=500M
改完配置后执行 systemctl restart systemd-journald 让配置生效。我在我们自己的机器上统一设成500M,对生产环境排障来说,保留最近几天的关键日志基本够用了,也不会出现日志把磁盘写满的事故。
3. 服务管理不只有启停:systemctl的进阶用法与失败定位
systemctl的启停大家都会,但很多人的使用习惯还停留在“start/stop/restart/status”四件套。我承认这四件套日常够用,但真要排障时,systemd提供的一堆状态查询命令比四件套高效得多。
3.1 从失败状态反向找配置问题
接手一台别人维护的机器,第一件事永远是看有没有服务处于失败状态:
bash复制systemctl list-units --type=service --state=failed
这个命令会把所有启动失败的服务列出来,一次就能看完,比逐个systemctl status高效太多。我接过一台测试服务器,它就是某个内部服务启动失败,但这个服务不在常规巡检范围里,要不是我用这个命令扫描一遍,根本发现不了。
定位到失败的服务后,再用 systemctl status --full 查看详细状态信息。这里我建议加上 --full,因为默认status输出会截断长行的关键信息。
还有一个很多新手不知道的命令:systemctl cat 服务名。它会把当前生效的 unit 文件完整打印出来,包括主配置和覆盖配置。如果你想知道这个服务到底以什么参数运行、ExecStart指向哪里,这条命令比去 /etc/systemd/system 目录里翻文件要快,而且它还带注释,可读性更强。
bash复制systemctl cat nginx
改过unit文件之后,有一件事必须执行,否则配置文件改了等于没改——systemctl daemon-reload。它让systemd重新加载unit文件。我见过不少人在改了 tomcat 的启动内存参数后,执行restart发现参数没生效,折腾半天才想起来忘了daemon-reload。这个操作本身不重启任何服务,但它是让改动生效的前置条件。
3.2 开机顺序与启动耗时分析
系统启动变慢,想找到底是哪个服务拖了后腿,可以用 systemd-analyze blame。这条命令会列出所有服务单元按启动耗时的降序排列,一眼就能看到最耗时的前几名。
bash复制# 查看各服务启动耗时
systemd-analyze blame | head -10
# 查看关键启动链路的依赖关系与耗时
systemd-analyze critical-chain
critical-chain 则更进一步,它会显示从目标单元到系统启动目标的整个依赖链,并标注每一层的耗时。有一次我排查一台虚拟机启动要5分钟的问题,用blame看到最耗时的是一个网络文件系统挂载服务,它默认在启动时会等待挂载超时,那个超时设置得特别长,后来把这个服务的挂载参数改成了noauto+后台重连,启动时间直接压到了1分钟以内。
3.3 服务账号、端口与进程的三方对齐
排障时经常遇到“端口被占用了,但不知道被谁占了”的问题。传统做法是netstat,但现在更推荐 ss,结合systemd还能进一步找到对应的服务单元。
bash复制# 查看所有监听中的TCP端口及对应进程
ss -lntp
# 查看指定服务的MainPID
systemctl show -p MainPID nginx
ss -lntp 有多行,重点看State列为LISTEN的行,以及Process列的PID和进程名。如果这个PID对应的进程是服务子进程而不是主进程,你可以再用 systemctl show -p MainPID 判断它是不是当前unit的直接管理对象。实际工作中我遇到过“服务重启了但旧进程还在监听”的情况,先用ss找到旧PID,再看它的父进程PID,发现是原来的守护进程没退干净,直接kill掉旧进程才算解决。这种问题只看端口占用是发现不了的,必须把端口、进程、服务单元三者对应起来看。
4. 网络不通,先按链路来:从ping、ss到tcpdump的分层排查法
网络问题是最容易让人方向错乱的排障类型。ping不通到底是链路断了、路由不对、DNS解析失败还是防火墙拦截,新手往往一头雾水。我的做法是严格按链路分层排查,每一层用不同命令验证,绝不跳过步骤直接猜。
4.1 连通性与路由:ping、traceroute、ip route
第一层确认物理和链路连通性。ping 是最基础的探测工具,但实际使用时建议加参数,不要裸敲一条ping跑了:
bash复制# 发送3个包,每个包最大等待1秒
ping -c 3 -W 1 目标IP
-c 限制发送包数量,避免Ctrl+C都来不及收场;-W 设置超时,防止长时间无响应堵塞排障流程。ping通说明链路层和IP层没问题,但“能ping通”不代表“服务没问题”,它只验证了ICMP协议可用。
链路层的更细致排查用 traceroute -n,-n 参数禁止反向解析域名,纯数字IP输出,速度会快很多。如果发现某一跳出现 * * *,注意不要立刻断定是故障——很多运营商路由器出于性能考虑不响应TTL超时,* 可能是正常现象,关键是看后面的跳数能否到达最终目标。
路由表的确认用 ip route,它替代了旧的 route -n 命令:
bash复制ip route show
确认默认路由存在,网卡配置了正确的IP,下一跳可达,再往下一层排查。
4.2 端口与服务:ss才是今天的标准工具
第二层确认端口监听状态和服务健康状态。ss 命令前面提到过一次,这里展开说。它比netstat更快,信息也更全面,尤其是在socket数量多的时候,ss的速度优势非常明显。
bash复制# 查看所有TCP监听端口,显示进程信息
ss -lntp
# 查看TCP连接状态汇总
ss -s
ss -s 输出的连接统计能快速判断当前系统TCP连接的整体情况,比如SYN-SENT堆积,说明发起的连接请求迟迟没有收到回应,通常意味着对端防火墙拦截或对端服务过载。
HTTP和HTTPS服务还可以用 curl 做主动探测,关键是使用 -w 参数输出时间分解,很多“服务慢”的问题靠这个命令能直接定位到具体环节:
bash复制curl -o /dev/null -s -w "状态码:%{http_code} 连接耗时:%{time_connect}s 总耗时:%{time_total}s\n" http://目标地址
如果 time_connect 很高,说明TCP三次握手阶段就有问题,大概率是网络延迟或SYN队列溢出;如果 time_connect 正常但 time_total 很高,说明是服务端处理速度慢,跟网络关系不大。这一条命令能给整个排障指明方向。
4.3 抓包确认问题:tcpdump最少必要用法
前面所有命令都只能观察到统计结果,如果问题依然定位不了,最后的手段才是抓包。tcpdump 是最常用的抓包工具,但很多人一上来就害怕它的各种参数。其实只要掌握两组用法,日常排查完全够用:
bash复制# 实时抓取本机80端口的流量,不解析域名,抓20个包后自动退出
tcpdump -i any port 80 -n -c 20
# 抓包并保存到文件,方便后续慢慢分析
tcpdump -i eth0 host 192.168.1.100 -w capture.pcap
抓包文件可以用 tcpdump -r capture.pcap 读取,也可以拖到Wireshark里可视化分析。用tcpdump排查TCP重传和连接失败时,重点看TCP包头里的SYN和ACK标志:如果客户端连续发多个SYN但一直没有收到SYN-ACK,基本可以对端防火墙在静默丢包;如果SYN-ACK收到了但后面的数据包一直重传,可能涉及MTU问题或者中间设备丢包。
抓包有个原则:先用前面的命令缩小范围,再决定抓包位置和过滤条件,千万别上来就全量抓包然后无目的地翻,那样效率极低。
5. 批量文本处理:管道、xargs、sort、uniq的组合拳
Linux命令的魅力在于组合。单条命令能力有限,但通过管道把几条命令串起来,能解决很多日常的批量处理需求。这一节里我挑几个组合频率最高的命令,用实际场景把它们串起来讲。
5.1 管道思维与xargs传参
管道 | 的本质是把前一个命令的标准输出接到后一个命令的标准输入。但有个问题:很多命令(比如rm、cp、mkdir)并不从标准输入读数据,而是接收命令行参数。这时候就需要 xargs 来搭桥,把前一个命令的输出转换成后一个命令的参数。
bash复制# 删除 /tmp 目录下所有 .log 文件,并逐个打印
find /tmp -name "*.log" -type f | xargs -n 1 echo
# 批量打包指定文件
find /data/logs -name "*.log" -mtime +3 | xargs -I {} mv {} /data/archive/
xargs 有两个常用参数值得记住:-n 1 表示每次只传一个参数给后一个命令;-I {} 指定占位符,适合需要把参数放在命令中间位置的场景。还有一个安全相关的小细节:当文件名可能包含空格时,直接 find | xargs 会把一个带空格的文件名拆成两个参数,导致命令报错。解决办法是 find ... -print0 配合 xargs -0,以空字符作为分隔符而不是换行符。
bash复制find /data -name "*.log" -print0 | xargs -0 rm -f
5.2 排序、去重与计数:统计access.log请求最多的IP
日志分析是Linux命令组合拳最经典的练兵场。比如要统计nginx access.log里请求次数最多的前10个IP,一行命令就能完成:
bash复制awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
这里每一级管道都有明确的作用:
awk '{print $1}':从日志里取出第1列,nginx默认日志格式中第1列就是客户端IP。sort:把相同的IP排到一起,排序是uniq去重的前置条件。uniq -c:统计相邻相同行的数量,输出格式是“次数 IP”。sort -rn:按数字逆序排列,让出现次数最多的排在最前。head -10:只取前10条。
这个组合里最关键的一步是 sort,很多人会忘记先排序直接用 uniq -c,结果统计结果完全错乱。uniq只能去重相邻的重复行,必须先sort把相同的行聚到一起,这一点是新手最容易踩的坑。
sort本身也很有讲究。排序有几种常用模式:-n 按数值排序,-h 按人类可读的数字(如1K、2M)排序,-t 指定分隔符,-k 指定排序的列。比如排du输出的目录大小:
bash复制du -sh /data/* | sort -h
用 -h 而不是 -n,是因为du输出的是“500M”“2G”这种带单位的字符串,直接按数值排会乱,-h 能按实际大小正确排序。
5.3 tee与comm:不打断管道也能保存中间结果
管道组合中还有一个容易被忽略的命令 tee。它的作用是把标准输出同时写入文件和标准输出,相当于把数据流“分叉”了一下。比如数据分析流程里,又想保留中间结果,又想继续往下一个命令传:
bash复制tail -f /var/log/nginx/access.log | tee /tmp/access_live.log | awk '{print $1}' | sort | uniq -c
这条命令既能实时保存全量日志到一个文件,又能把处理管道后的统计结果输出,一举两得。
comm 是一个比较冷门但好用的命令,它用来比较两个已排序文件的公共行:
bash复制comm -12 /tmp/list_a.txt /tmp/list_b.txt
-12 表示去掉只在第一个文件和只在第二个文件里的行,只输出两个文件的交集。我在核对两台服务器的软件包差异、对比两份机器里文件清单的场景用过它。注意的是它要求两个输入文件都必须先sort过,否则结果乱得像彩票开奖。
6. 定时任务:crontab不是唯一选项,systemd timer和at各有分工
说到定时任务,大部分人的第一反应就是crontab。确实,crontab使用简单、世代相传,但它也有一些固有的局限:最小粒度只能到分钟、不感知服务依赖、环境变量默认很精简。实际的运维场景里,除了crontab,at 和 systemd timer 也有各自的用武之地。
6.1 crontab的写法与三个经典坑
crontab的基本用法是 crontab -e 编辑当前用户的定时任务,crontab -l 查看,crontab -r 删除。时间字段从左到右分别是分、时、日、月、周:
bash复制# 每天凌晨3点30分执行备份脚本
30 3 * * * /opt/scripts/backup.sh
# 每5分钟执行一次监控脚本
*/5 * * * * /opt/scripts/monitor.sh >> /var/log/monitor.log 2>&1
这么多年用下来,我总结crontab最常见的三个坑:
第一个坑是环境变量问题。crontab执行任务时的PATH非常精简,跟你手动登进去的shell环境完全不是一回事。脚本里如果直接写 python 或者 docker,运行时大概率报“command not found”。解决办法是脚本开头用绝对路径,或者在crontab命令里先source环境变量文件。我自己的习惯是写成这样:
bash复制30 3 * * * /usr/bin/bash /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
脚本第一行也要写清楚shebang,尽量不用相对路径和裸命令名。
第二个坑是 % 符号。crontab的命令行中 % 会被解释成换行符,如果命令里要用日期格式化(比如 $(date +%Y%m%d)),直接写会出错。解决办法是把整条命令放进一个脚本文件,crontab只调用脚本;或者在 % 前面加反斜杠转义。
第三个坑是日志输出和重定向。crontab默认会把标准输出和错误输出发邮件给用户,如果系统没配邮件服务,日志就不知所踪。所以我写crontab几乎每一条都会带 >> 日志路径 2>&1,把输出重定向到文件,排障时才有据可查。
6.2 at、watch与systemd timer的合适场景
at 用于一次性定时任务。比如凌晨2点要做一次数据库维护,但你人没办法爬起来执行,就可以在下班前设置好:
bash复制echo "/opt/scripts/db_clean.sh" | at 02:00
at的用法是先输入命令再指定执行时间,时间格式也很灵活,at now + 5 minutes、at 22:30 都行。它还支持 atq 查看等待队列,atrm 编号 删除任务。说实话at在今天的实际使用频率不算高,但它是处理“只跑一次”需求时的最优解,比临时加一条cron再手工删优雅得多。
watch 则是周期性地执行命令并刷新显示,适合观察变化趋势。比如实时观察内存变化:
bash复制watch -n 1 free -h
watch适合排查问题时观察输出是否持续变化,比如看某个服务进程的CPU占用是否下降、端口是否从监听状态变成关闭状态。它不是一个真正的调度工具,但它填补了“高频观察”这个场景空白。
systemd timer是更接近“正经定时任务”的方案。它比crontab灵活的地方在于:可以精确到秒级别、可以依赖其他服务启动完成后触发、可以查看上一次运行的结果,还有更细致的失败重试策略。它的基本单位是一对.service和.timer文件。比如要每小时执行一次 /opt/scripts/report.sh,可以这样写:
bash复制# /etc/systemd/system/report.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/report.sh
# /etc/systemd/system/report.timer
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
启用方式:
bash复制systemctl enable --now report.timer
查询timer触发状态:
bash复制systemctl list-timers
list-timers 这个命令能看到每个timer下次触发时间和上次触发结果(如succeeded/failed)。crontab是跑了就跑了,跑完没有统一的地方看状态,全凭你自己日志;systemd timer则把每次运行结果纳入systemd体系管理,出问题时在journalctl里一目了然。如果是新装的系统,我个人的倾向是:简单任务用crontab,涉及服务依赖或需要精确调度用systemd timer,一次性需求用at。
最后分享一个我坚持很久的习惯。每次排查完一个问题,我都会把当时用的命令和关键输出按日期存到一个文本文件里,文件名就叫 troubleshooting-2025-xx-xx.log。几个月后翻出来看,很多当时要现查的命令,现在闭着眼睛都能写;更重要的是,同样的问题第二次出现时,十分钟内就能定位完。Linux命令这东西,背十遍不如真正用一遍。如果你也正处在“命令都认识、系统看不懂”的阶段,建议从今天内容里的四个场景开始,找台测试机模拟一遍,比收藏一百篇文章都有用。
