做了这么多年运维和开发,发现一个有意思的现象:网上Linux命令的文章一抓一大把,但大家搜来搜去,日常碰到的问题翻来覆去就那么几个。前六篇我们把用户管理、文件操作、进程控制这些基础打完了,这一篇我打算换个思路,不按命令字母表硬排,而是直接把工作中最高频、最容易出事的场景拎出来,以解决实际问题为主线,把那些常用命令揉碎了讲。比如为什么find一跑就把机器卡死、rm删除后怎么恢复、scp传文件到一半断了怎么办、磁盘IO被打满从哪里查起——这些都是群里的兄弟踩过坑之后问得最多的问题,也是面试官最爱拿来出题的考点。
这篇内容会稍微有些长,建议先收藏再慢慢读。不管你是刚接手服务器的新手运维,还是被临时拉去排查问题的后端开发,又或者是准备面试的候选人,把这篇里涉及的命令和思路过一遍,实际干活的时候会顺手很多。
1. 文件查找与磁盘占用:小心你的find和rm
1.1 find查找命令的完整用法与性能陷阱
find是Linux里最强大的文件查找工具,没有之一。但正因为强大,很多人的用法其实是有问题的。
最基本的语法是find [路径] [表达式],路径就是你要从哪里开始找,表达式就是查找条件。举个例子,我想找出/etc目录下所有后缀为.conf的文件:
bash复制find /etc -name "*.conf"
这个命令看起来简单,但里面藏着两个大坑。第一个坑是通配符的转义,-name后面的*.conf建议加上引号。你直接写find /etc -name *.conf在某些shell环境下,如果当前目录刚好有匹配的文件名,shell会在find执行前就把通配符展开,导致find收到的参数变成一堆具体文件名,结果完全不对。所以我习惯一律写成find /etc -name "*.conf",稳妥。
第二个坑是性能。find / -name "xxx"这种全盘扫描,在生产环境的机器上执行,很容易把磁盘IO直接拉满,导致线上服务卡顿甚至超时。我之前遇到过一起事故,同事想找一个配置文件,直接在根目录执行了全盘find,结果数据库所在主机的IO被打爆,业务接口大量超时报警。所以我现在养成一个习惯:查找前先想清楚范围,能用find /home就不用find /,能限定目录层级就加-maxdepth参数。
按时间维度查找也是日常高频场景,比如查最近7天内被修改过的文件:
bash复制find /var/log -mtime -7
-mtime表示修改时间,后面的数字加减号有讲究:-7表示7天以内,+7表示7天以前,直接写7就精确表示第7天当天。类似的还有-atime(访问时间)、-ctime(状态变更时间)。还有一个参数是-mmin,按分钟来计算,排查问题的时候用得比较多,比如“这10分钟内哪个文件被动了”:
bash复制find /app -mmin -10
如果按文件大小查找,常见面试题“找出大于100MB的文件”:
bash复制find / -type f -size +100M
这里的单位很关键,M是MB,G是GB,k是KB,注意小写k。+100M表示大于100MB,-100M表示小于100MB,不带符号就是精确等于100MB。
find还有一个高频组合是配合-exec执行操作,比如一键清理7天前的日志备份:
bash复制find /data/backup -name "*.tar.gz" -mtime +7 -exec rm -f {} \;
这里{}代表find找到的每一个文件,\;是-exec命令的结束标记。需要注意这里的分号和反斜杠不能丢,丢了会直接报错。用-exec有个潜在风险:如果文件数量特别多,会逐条执行命令,效率偏低,更推荐用管道配合xargs:
bash复制find /data/backup -name "*.tar.gz" -mtime +7 | xargs rm -f
但xargs有个天坑:如果文件名里有空格或特殊字符,会被错误拆分。稳妥的写法是:
bash复制find /data/backup -name "*.tar.gz" -mtime +7 -print0 | xargs -0 rm -f
-print0和-0搭配,用空字符作为分隔符,就能完美兼容带空格的文件名。这个细节很多人不知道,操作线上服务器时不小心就会踩雷。
1.2 du和df:磁盘空间到底被谁吃掉了
排查磁盘空间不足,第一步用df看整体情况:
bash复制df -h
-h是人性化显示,会自动把字节数换算成G、M这种单位。输出的第一列是文件系统,最后一列是挂载点,重点看/根分区和/home这种可能单独分区的挂载点有没有被打满。
df看到的是文件系统层面的总空间,但要查具体是哪个目录占了大头,就得靠du了。du默认会递归统计所有子目录的大小,所以直接du -sh /会很慢,通常的做法是先看一级目录:
bash复制du -sh /* 2>/dev/null | sort -rh | head -20
-s表示只汇总每个参数目录的总大小,-h人性化显示,sort -rh按人类可读的数字反向排序,head -20只取前20行。这样一趟下来,哪个目录是空间杀手一目了然。
像/var/log下面日志文件积累导致根分区满,用这个命令组合几分钟就能定位。需要注意的坑是du会真实读取所有文件元数据,在IO已经紧张的情况下执行,会暂时加重负载,建议低峰期排查。
还有一种情况:df -h显示空间剩余很多,但应用却报“磁盘空间不足”,那大概率是inode用完了。检查inode的命令是:
bash复制df -i
如果IUsed一列的百分比接近100%,就是大量的小碎文件占满了inode,最经典的场景是邮件队列、缓存目录和docker的存储目录堆积了大量小文件。处理方式是找到对应目录,清理掉那些没用的临时文件,然后用df -i确认恢复。
1.3 rm删除命令的安全操作与误删恢复思路
rm -rf被戏称为“删库跑路专用命令”,不是没道理的。我见过太多人敲完rm -rf /var/log/nginx/之后发现路径多打了个空格,变成了rm -rf /var/log/nginx /。这种惨案的根源是:rm命令本身没有“回收站”概念,删了就是真的没了,ext4和xfs文件系统下常规手段基本没法恢复。
所以我的建议有三条,可以说都是血泪经验换来的。
第一条,删除之前先ls确认。把这个习惯刻进肌肉记忆:
bash复制ls -ld /data/old_app
rm -rf /data/old_app
第二条,能用mv替代就尽量用mv。把要删的目录先移动到一个临时trash目录,观察几天,确认业务没依赖了再真正删除:
bash复制mkdir -p /data/.trash
mv /data/old_app /data/.trash/
第三条,重要数据务必做异地备份。rm误删之后,如果文件被进程持续占用,可以用lsof找回部分数据,比如:
bash复制lsof | grep deleted
然后到/proc/<pid>/fd/目录下找到对应的文件描述符,复制出来恢复。但这属于“死马当活马医”的办法,操作复杂且依赖进程存活,不能当作常规手段。真正安全的策略永远是备份先行。
提示:线上环境执行任何批量删除命令,建议先加上
-v参数打印删除日志,并且用find先统计一下数量,不要闷头一把梭。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文本处理三剑客:grep、sed、awk的实战玩法
2.1 grep筛选日志:比你想象中更能打
grep应该是Linux下使用频率最高的命令了。常规用法是grep 关键词 文件名,但实际日志分析中,很多人只会这一步,浪费了这个工具一大半的能力。
最常用的几个参数:
bash复制grep -i "error" app.log # 忽略大小写匹配
grep -r "TimeoutException" /app/logs/ # 递归搜索整个目录
grep -n "ERROR" app.log # 显示匹配行行号
grep -v "heartbeat" app.log # 反向匹配,过滤掉不需要的行
grep -c "ERROR" app.log # 统计匹配行数
grep -A 5 -B 5 "NullPointerException" app.log # 显示匹配行的后5行和前5行
-A和-B这两个参数在排查报错时特别有用。比如你在日志里看到一条异常堆栈,只看报错那一行根本无从下手,带上上下文才能还原当时的代码执行路径。
grep处理多文件时,可以在文件名前加-H强制显示文件名,或者用--include限定文件类型:
bash复制grep -r "orderId:12345" /app/logs/ --include="*.log" -l
-l表示只列出包含匹配内容的文件名,而不输出具体匹配行。这个在“日志分散在多个滚动文件里,想知道哪个文件出现了某条记录”时极其好用。
要注意一个grep的经典误区:处理大文件时,grep本身很快,但如果你的正则写得太复杂,比如grep -E "^[0-9]{4}-[0-9]{2}-[0-9]{2}.*ERROR.*orderId=(123|456)"这种嵌套分组,性能会急剧下降。更推荐的做法是先grep ERROR缩小范围,再对结果做二次处理,即“管道接力”而不是“一个正则打天下”。
2.2 sed替换和删除:脚本化操作文本
sed是流编辑器,擅长对文本流做增删改。日常工作里最常用的是替换操作:
bash复制sed -i 's/old_string/new_string/g' config.conf
-i表示直接修改文件,s/表示替换,g表示全局替换(不加的话每行只替换第一处匹配)。这句话要慎用,建议先去掉-i跑一遍看输出结果,确认无误再加-i真正落盘:
bash复制sed 's/old_string/new_string/g' config.conf
比如把配置里所有8080端口改成9090:
bash复制sed -i 's/8080/9090/g' nginx.conf
按行号删除也是高频操作,比如删除文件第10到20行:
bash复制sed -i '10,20d' data.txt
这里的d是删除指令,前面加上行号范围。还有一个好用的替代是在file.conf里直接注释掉包含某个关键字的行:
bash复制sed -i '/^server_name/c\# server_name disabled' nginx.conf
当你需要批量修改几十台服务器的配置文件时,sed -i配合循环就是生产力工具。但要留个心眼:不同版本的sed对-i参数后面是否跟备份后缀的处理有差异。macOS自带的BSD sed要求-i后面必须跟一个参数才能正常运行,而Linux上的GNU sed可写可不写。跨平台时我一般写成sed -i.bak 's/a/b/g' file,这样既兼容两种情况,还能自动生成一个.bak备份文件,一举两得。
2.3 awk取列和统计:一行命令做出Excel的活
awk的行处理逻辑是:按行读取,默认按空格(或制表符)切分成多列,$1就是第一列,$2就是第二列,NF表示当前行的列数,NR表示当前处理到第几行。最经典的应用是取日志里的IP和状态码:
bash复制awk '{print $1, $9}' access.log
nginx默认日志格式里,$1是客户端IP,$9是HTTP状态码。这句命令就把日志简化成了“IP + 状态码”两列。
如果要统计每种状态码的数量:
bash复制awk '{count[$9]++} END {for (code in count) print code, count[code]}' access.log
这里用到了awk的关联数组特性,count[$9]以状态码为键进行累加,END块在处理完全部行后执行输出。这个用法在处理统计场景时效率很高,不用写复杂的shell循环。
如果日志格式里字段不是单纯空格分隔,可以用-F指定分隔符,比如用逗号分隔的CSV:
bash复制awk -F',' '{print $2}' data.csv
awk还支持先过滤再处理:
bash复制awk '$9 >= 500 {print $1}' access.log | sort | uniq -c | sort -rn
这条命令的具体含义是:找出所有5xx错误请求的IP,然后利用sort和uniq -c统计每个IP的出现次数,最后按次数逆序排列,一眼就能看到哪个IP在频繁触发服务器错误。这是排查恶意请求和异常流量非常实用的命令组合,建议记下来。
三剑客不是孤立存在的,grep负责筛选、sed负责修改、awk负责统计,配合管道符号组合使用,威力巨大。我个人最常用的日志分析三板斧是:
bash复制# 1. 统计每个IP的访问次数,取前10
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 2. 查看某个接口的响应时间分布
grep "/api/order" access.log | awk '{print $NF}' | sort -n | head -50
# 3. 找出耗时超过2秒的请求
grep "/api/order" access.log | awk '$NF > 2 {print $0}' | wc -l
3. 用户与权限管理:系统安全的第一道门
3.1 新建用户、用户组和sudo授权
新入职的员工、新部署的服务,经常要新建系统用户。最简单的新建用户命令是:
bash复制useradd zhangsan
passwd zhangsan
useradd zhangsan只是创建了一个账号,真正设置登录密码的是passwd。很多人在这块会困惑:为什么用户建好了,却登录不上?大概率就是忘了执行passwd设置密码。
如果想让新用户拥有一个完整的家目录和默认shell,建议用:
bash复制useradd -m -s /bin/bash zhangsan
-m会创建/home/zhangsan家目录,-s /bin/bash指定登录shell为bash。不指定-s的话,系统会使用默认shell,在某些精简系统里默认可能是/bin/sh,使用体验会有差异。
创建用户组用groupadd:
bash复制groupadd devops
usermod -aG devops zhangsan
usermod -aG中的-a是追加的意思,这个-a非常关键!如果不加-a,-G会把用户从原有附属组中全部移除,只保留你指定的组。我见过有人想把用户加到docker组,结果敲成了usermod -G docker zhangsan,用户原有的sudo权限直接没了,紧接着就是一堆权限报错。所以这里统一建议:加附属组永远用-aG。
给用户授予sudo权限,推荐做法不是直接编辑/etc/sudoers,而是把授权文件放到/etc/sudoers.d/目录下:
bash复制echo "zhangsan ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/zhangsan
chmod 440 /etc/sudoers.d/zhangsan
NOPASSWD:ALL表示sudo时不用输密码,属于最高权限授权。生产环境更推荐按需授权,比如只允许以某个特定用户身份执行特定命令:
bash复制echo "zhangsan ALL=(root) /bin/systemctl restart nginx" > /etc/sudoers.d/zhangsan
这样zhangsan就只能用root身份重启nginx,无法执行其他root命令,风险范围可控。这也是等保测评里关于最小权限原则的常见要求。
3.2 chmod和chown:看懂权限位的数字逻辑
chmod用来修改文件权限,chown用来修改文件属主和属组。Linux的权限分为读(4)、写(2)、执行(1)三档,数字相加得到权限值。755代表属主可读可写可执行,属组和其他人可读可执行;644代表属主可读可写,其他人只读。这是最常见的两组默认权限。
修改属主和属组:
bash复制chown zhangsan:devops /data/app
递归修改目录下所有文件用-R:
bash复制chown -R zhangsan:devops /data/app
chmod -R 755 /data/app
需要特别注意一点:chmod -R会把目录下所有文件都设置成可执行权限,包括普通文本文件,这在安全性要求高的场景是不合理的。更精细的做法是用find配合chmod,分别处理目录和文件:
bash复制find /data/app -type d -exec chmod 755 {} \;
find /data/app -type f -exec chmod 644 {} \;
这样目录统一755,普通文件统一644,既保证可读性又不引入多余的执行权限。这也是规范化的日常操作习惯,建议直接写进自己的命令手册。
还有一个高阶场景是setfacl设置ACL(访问控制列表)。当某目录需要同时授权给多个用户不同权限,而传统chmod只有owner/group/others三类无法满足时,ACL能精确配置:
bash复制setfacl -m u:lisi:rwx /data/shared
配置完成后用getfacl /data/shared查看生效情况。ACL在生产环境的共享目录场景非常实用,不少面试题也会涉及。
4. 网络诊断与远程传输:scp、rsync和ss的实战细节
4.1 scp和rsync:文件传输的选型与断点续传
跨机器拷贝文件,scp是大家最先接触的命令:
bash复制scp /data/file.tar.gz root@192.168.1.10:/data/
这表示把本地的file.tar.gz推送到远程服务器的/data/目录。反向拉取,把远程文件拉到本地:
bash复制scp root@192.168.1.10:/data/file.tar.gz /local/path/
scp还支持-P指定端口,注意这里是大写P:
bash复制scp -P 2222 /data/file.tar.gz root@192.168.1.10:/data/
传整个目录加-r:
bash复制scp -r /data/app/ root@192.168.1.10:/data/
scp的问题在于:一次性任务还好,但中断之后不能续传,大文件传输到一半网络闪断就要从头再来。所以传输大文件或者做备份同步时,我一般用rsync替代。rsync最常用的参数组合:
bash复制rsync -avzP --progress /data/app/ root@192.168.1.10:/data/app/
解释一下:-a归档模式保留权限、时间戳等属性,-v显示详细信息,-z传输时压缩,-P整合了--partial(保留部分传输文件)和--progress(显示进度条)。传输中断后重新执行同一句命令,它会只同步缺失的部分,体验比scp友好太多。
rsync还有一个杀手级场景是删除大量文件。当rm -rf删文件特别慢时,可以建一个空目录,然后:
bash复制rsync -a --delete 空目录/ 目标目录/
利用rsync的--delete参数把目标目录中多余的文件清空,速度通常比rm -rf快很多,处理上百万小文件时尤其明显。
4.2 ss和curl:快速定位网络问题
老派的netstat命令很多教程还在讲,但实际新系统上我推荐用ss替代。ss输出更快、信息更全,语法上兼容度高。
查看所有监听端口:
bash复制ss -lntp
-l显示监听中的socket,-n不做域名解析直接显示IP和端口(速度快很多),-t只看TCP,-p显示对应的进程信息。加-u可以看UDP,比如排查DNS服务时:
bash复制ss -lunp
查看当前所有TCP连接状态统计:
bash复制ss -s
当服务器出现大量TIME_WAIT或CLOSE_WAIT连接时,ss -s能快速给出全局概览。如果TIME_WAIT异常多,常见原因是短连接请求量太大,服务端主动关闭连接,可以结合sysctl调整内核参数,这个问题很多架构师面试时也会问。
curl也远不止“访问网页”这一个用途。日常排错最常用的几个参数:
bash复制curl -I https://example.com # 只查看响应头
curl -v https://example.com # 打印详细过程(握手、请求头、响应头全展示)
curl -X POST -H "Content-Type: application/json" \
-d '{"key":"value"}' https://example.com/api # 发送POST请求
curl -w "耗时: %{time_total}s\n" https://example.com # 统计总耗时
curl -w可以自定义输出格式,我经常用它来检测接口的各个阶段耗时,比如DNS解析、TCP连接、TLS握手、首字节时间等:
bash复制curl -o /dev/null -s -w "DNS: %{time_namelookup}s, TCP连接: %{time_connect}s, TLS握手: %{time_appconnect}s, 首字节: %{time_starttransfer}s, 总耗时: %{time_total}s\n" https://example.com
这条命令的输出在定位“页面到底是DNS慢还是后端响应慢”时特别好用,建议收藏。-o /dev/null表示丢弃响应体,-s静默模式去掉多余输出,-w指定打印格式。
4.3 telnet和nc:端口连通性排查
排查“服务器之间能不能连通某个端口”,telnet是最简单粗暴的工具:
bash复制telnet 192.168.1.10 3306
如果端口通,会显示Connected to;如果不通,会一直卡住直到超时。但telnet在部分最小化安装的系统里没有预装,可以用nc(netcat)替代:
bash复制nc -zv -w 3 192.168.1.10 3306
-z表示扫描模式不发送数据,-v显示详细信息,-w 3设置3秒超时。批量检测多个端口:
bash复制nc -zv -w 2 192.168.1.10 22 80 443 3306 6379
这条命令一次就能告诉你目标主机上哪些端口开放、哪些不通,排查防火墙策略和网络隔离时效率很高。
5. 软件包管理与服务部署:从安装到守护进程
5.1 apt、yum和dpkg、rpm的依赖关系
不同Linux发行版用的包管理器不一样。Debian系(Ubuntu、Debian)用apt,RedHat系(CentOS、Rocky、Fedora)用yum或dnf。基础命令对照如下:
| 操作 | Debian系 | RedHat系 |
|---|---|---|
| 更新软件源 | apt update |
yum makecache |
| 升级所有包 | apt upgrade |
yum update |
| 安装软件 | apt install nginx |
yum install nginx |
| 卸载软件 | apt remove nginx |
yum remove nginx |
| 搜索软件 | apt search nginx |
yum search nginx |
| 查看已安装 | dpkg -l |
rpm -qa |
| 查看某个包是否安装 | dpkg -l nginx |
rpm -q nginx |
| 查看文件属于哪个包 | dpkg -S /usr/bin/nginx |
rpm -qf /usr/bin/nginx |
依赖处理上,apt和yum都会自动分析并安装依赖,这是它们相对源码编译最大的优势。但偶尔也会碰到依赖损坏,比如Debian系报unmet dependencies,常规救援手段是:
bash复制apt --fix-broken install
apt -f install
RedHat系如果是本地rpm包安装时缺依赖,可以用:
bash复制yum localinstall ./xxx.rpm
让它自动从软件源拉取依赖,而不是直接rpm -ivh xxx.rpm然后被依赖问题卡死。
5.2 systemctl:服务管理和开机自启的底层逻辑
现代Linux发行版的服务管理基本都被systemd统一了,日常操作就几个命令:
bash复制systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl status nginx
systemctl enable nginx
systemctl disable nginx
enable是设置开机自启,disable取消自启。注意:只start不enable,服务当前能跑,但服务器重启后就起不来了,这是部署时最容易漏的一步。
查看所有服务状态:
bash复制systemctl list-units --type=service --state=running
查看某个服务启动失败的具体原因:
bash复制systemctl status nginx.service
journalctl -u nginx.service -n 50
journalctl是systemd的日志工具,-u指定服务单元,-n 50表示只看最近50行。很多服务启动失败的报错信息就被吞在这里面,比status的输出详细得多。
服务启动失败时,第一反应不要反复restart,先看配置是否正确。比如nginx,可以用:
bash复制nginx -t
这个命令会检查配置文件语法,并告诉你错误出在第几行。同理,很多其他服务的守护进程也提供了类似的配置自检参数,养成修改配置后先自检再reload的习惯,可以少踩很多坑。
5.3 高频部署场景:安装Python、Nginx和Docker
热词里有好几个人搜“Linux系统安装Python”、“linux安装nginx”、“linux安装docker”,这三个场景我合并到一起说,都是新手部署时反复踩坑的地方。
安装Python的坑主要在于版本。直接apt install python3装到的一般不是最新版,如果你需要指定版本,推荐用源码编译。以Python 3.11为例:
bash复制# 安装编译依赖
apt update && apt install -y build-essential libssl-dev zlib1g-dev libncurses5-dev \
libffi-dev libsqlite3-dev libbz2-dev
# 下载源码并编译
wget https://www.python.org/ftp/python/3.11.8/Python-3.11.8.tgz
tar xzf Python-3.11.8.tgz
cd Python-3.11.8
./configure --enable-optimizations --prefix=/usr/local/python311
make -j$(nproc) && make install
# 建立软链接
ln -s /usr/local/python311/bin/python3.11 /usr/local/bin/python3.11
这里--enable-optimizations会做PGO优化,编译时间会长一些,但运行性能更好。make -j$(nproc)利用所有CPU核心并行编译,能明显缩短等待时间。如果编译过程中报ModuleNotFoundError: No module named '_ssl',基本都是libssl-dev没装,回头补上再重新编译即可。
安装Nginx,如果系统源里的版本够用,直接:
bash复制apt install -y nginx
systemctl enable --now nginx
注意systemctl enable --now是“启用并立即启动”二合一,比分开执行两条命令少走一步。如果官方源版本太旧,需要添加Nginx官方源:
bash复制# 以Ubuntu为例
echo "deb http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" > /etc/apt/sources.list.d/nginx.list
然后导入GPG key再apt update && apt install nginx。这里的坑是lsb_release -cs输出的是发行版代号,不同版本对应的Nginx源路径不同,版本不匹配会直接安装失败。
安装Docker,虽然各发行版软件源里可能自带,但强烈建议用官方脚本或官方源:
bash复制curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
systemctl enable --now docker
装完以后,把当前用户加入docker组,避免每次都要sudo:
bash复制usermod -aG docker $USER
newgrp docker
docker run hello-world
newgrp docker的作用是让当前会话立即生效新组,不用退出重登。加组这个步骤很多教程没写,导致用户每次执行docker命令都要加sudo,非常影响体验。
Docker的常用命令这里整理一份速查:
bash复制docker ps -a # 查看所有容器
docker images # 查看本地镜像
docker pull nginx # 拉取镜像
docker run -d -p 80:80 --name web nginx # 启动容器,后台运行并映射端口
docker exec -it web /bin/bash # 进入正在运行的容器
docker logs -f web # 查看容器日志
docker rm -f web # 强制删除容器
docker rmi nginx # 删除镜像
docker system prune # 清理悬空资源
5.4 另一个高频话题:从源码还是包管理器安装
新手经常纠结软件安装方式,这里给出我的选型建议。能用系统包管理器安装的,优先用包管理器,因为它有完善的依赖管理、卸载机制和版本控制,升级也方便。当需要特殊版本、定制编译参数或者做多版本共存时,才考虑源码编译。比如编译Nginx并添加第三方模块,就需要源码安装;而只是跑一个普通的WEB服务,源码编译就没必要,浪费时间也增加后续维护成本。
GitLab、MinIO、Prometheus这类服务,官方通常提供预编译的二进制包或deb/rpm包,直接下载安装,不要走源码编译,大坑会少很多。
6. 性能排查与日志分析:top、free、dmesg、journalctl
6.1 CPU、内存、磁盘IO问题排查三板斧
服务器“变卡了”是运维群里最常出现的问题,排查看起来高大上,其实第一步就是看资源到底被谁占了。
top命令是第一个要敲的。它默认按CPU使用率排序,实时刷新。进入top后按一下P按键可以按CPU排序,按M按键按内存排序。看load average的三个数值,分别代表1分钟、5分钟、15分钟的系统平均负载。如果1分钟数值明显高于15分钟,说明负载正在上升;如果三个数值都很高,说明问题持续了一段时间。
更精细的查看单核CPU情况,按1键盘会在top中展开所有CPU核心的占用率。如果只有某几个核被吃满,而其他核很空闲,大概率是线程绑定在特定CPU上的问题;如果所有核都吃满,那就是整体算力不足。
内存方面用free -h:
bash复制free -h
重点看available这一列,它是真正可用的内存。很多人会被used的数值吓到,其实现代Linux会缓存文件系统页面,这部分内存可以被回收,不用过度紧张。如果available接近0,同时swap使用量持续上升,说明物理内存确实不足,开始用到磁盘交换分区了。
磁盘IO查看iostat和iotop:
bash复制iostat -x 1 3
-x显示扩展统计信息,重点关注%util和await。%util接近100%说明磁盘已经满负荷运转,await数值过大说明IO等待时间太长。iotop则能直观看到哪个进程在疯狂读写磁盘,定位到PID之后顺藤摸瓜查业务即可。
6.2 内核日志和系统日志:别忽略dmesg和journalctl
很多服务器“莫名其妙”出问题,比如进程突然被杀、网卡断连、磁盘报错,答案往往藏在内核日志里。dmesg就是查看内核环形缓冲区的命令:
bash复制dmesg | tail -50
如果发现类似Out of memory: Killed process 1234 (java)的日志,说明是OOM Killer把进程干掉了,典型的物理内存不足或者cgroup内存限制触发。如果看到hung_task_timeout_secs这类错误,则可能是磁盘IO长时间无响应导致的。
查看系统日志总入口,使用journalctl:
bash复制journalctl -xe
-x附带解释信息,-e直接跳到日志末尾。看指定时间段的日志:
bash复制journalctl --since "2025-01-15 09:00:00" --until "2025-01-15 10:00:00"
这些命令组合起来,从系统内核、服务状态、应用日志三个层面把问题串起来,绝大多数故障都能找到根因,而不是靠重启和瞎猜。
6.3 进程管理:从ps到kill的全流程
查看进程的字段含义,ps是基础但重要的一块。最常用的组合是:
bash复制ps -ef | grep java
ps aux | sort -k3 -rn | head -10
ps -ef显示所有进程的标准格式,ps aux用BSD风格格式,sort -k3 -rn按CPU使用率排序。找到进程号之后,如果需要强制终止:
bash复制kill -9 PID
关于kill的信号,数值从小到大:kill默认发15号SIGTERM,先通知进程“请退出”;kill -9发9号SIGKILL,直接强制结束,不给任何善后机会。日常建议先用kill PID(即SIGTERM),等几秒看进程是否退出,不行再kill -9。某些服务对SIGTERM有优雅停机逻辑,会主动保存状态、释放连接,直接-9杀可能导致数据不一致或端口占用未及时释放。
如果进程变成了僵尸进程(ps输出中出现Z状态),父进程还没回收,可以尝试杀掉它的父进程来清理。僵尸进程本身不占CPU,但不清理会占着进程表项,积累多了会影响新进程的创建。
7. 其他高频场景:vim、git、gdb、k8s速查
其实这些工具每个都能单独写一篇长文,这里只把高频场景的命令按“记不住但必须会”的标准列出来,方便大家直接检索。
7.1 vim编辑器:面试和实战都绕不开的坑
vim是Linux环境下的编辑器之王,新手死在它手里很常见,因为它的模式切换和习惯完全不同。
模式切换:
bash复制vim file.txt # 进入后默认是普通模式
i # 进入插入模式,可以输入内容
Esc # 返回普通模式
:wq # 保存并退出
:q! # 不保存强制退出
在高频操作里,dd删除当前行、yy复制当前行、p粘贴、/keywords搜索并按n跳转下一个匹配,这几个是存活必备。全文替换命令:
bash复制:%s/old/new/g
这里%表示全文,和sed的正则语法类似。热词里提到“linux常用命令vi替换单词”,说的就是这个。注意冒号不能丢,否则光标直接移动到行首了,新手经常困惑半天。
7.2 git常用命令:企业协作必会清单
git命令很多,但日常开发在用的也就是那十几个:
bash复制git status # 查看工作区状态
git add . # 暂存所有改动
git commit -m "message" # 提交
git pull origin main # 拉取远端并合并
git push origin main # 推送
git branch feature/xxx # 创建分支
git checkout feature/xxx # 切换分支
git merge feature/xxx # 合并分支
git log --oneline -10 # 查看最近提交
git diff # 查看未暂存的改动
踩坑最多的是合并冲突。冲突发生时,git会在文件里标注类似<<<<<<< HEAD和>>>>>>> feature/xxx的标记,你需要手动删除这些标记、保留所需的代码块,再执行git add和git commit完成合并。不要试图绕过冲突直接强制提交,那是更深的坑。
7.3 gdb调试:嵌入式开发和后台崩溃排查的工具
热词里有人搜“gdb调试常用命令”,在Linux下排查程序崩溃问题时,gdb是不可替代的工具。基本流程是:
bash复制gdb ./a.out core
break main # 在main函数打断点
run # 运行程序
bt # 打印调用栈
next # 单步执行(跳过函数内部)
step # 单步执行(进入函数内部)
print 变量名 # 查看变量值
bt(backtrace)是每次程序崩溃后第一个执行的命令,它会把函数调用栈完整打印出来,直接定位到崩溃现场。配合core dump文件,可以直接看到崩溃时各个变量的值,这在排查段错误(Segmentation Fault)时几乎是唯一高效的路径。
7.4 k8s常用命令:容器编排环境的核心操作
如果你的环境是Kubernetes,那么“k8s常用命令”就是日常标配了。首先要记住的命名空间概念:kubectl默认操作default命名空间,查看其他命名空间需要加-n。
bash复制kubectl get pods -n kube-system # 查看指定命名空间的Pod
kubectl get nodes # 查看集群节点状态
kubectl get svc # 查看服务
kubectl logs -f podname # 查看Pod日志(加-f跟随输出)
kubectl describe pod podname # 查看Pod详细信息和事件
kubectl exec -it podname -- /bin/bash # 进入Pod容器
kubectl apply -f deployment.yaml # 应用配置文件
kubectl delete pod podname # 删除Pod(会自动重建,取决于控制器)
排错时,kubectl describe pod的输出里有Events部分,能直接看到镜像拉取失败、探针失败、Volume挂载错误等常见原因。而kubectl logs配合--tail=100只看最近100行,比直接拉全部日志高效得多。
8. 常见问题与排查技巧实录
8.1 高频问题速查表
把日常被问得最多的问题整理成一张速查表,方便对照排查:
| 问题现象 | 排查命令思路 |
|---|---|
| 磁盘空间显示满了但删不掉大文件 | lsof | grep deleted 查找被进程占用的已删除文件 |
| 端口被占用,启动失败 | ss -lntp | grep 8080 找到PID再决定kill |
| 命令找不到,提示command not found | 检查PATH,或使用which/type定位 |
| 修改配置后服务不生效 | 确认是否执行了systemctl restart,确认reload不等于restart |
| 远程连接慢或卡顿 | ss -tnp看连接状态,检查DNS解析和SSH配置 |
| nohup启动的程序退出后没了 | 检查是否用&后台,注意nohup是否置于管道前 |
| 脚本执行报Permission denied | chmod +x script.sh 或改用bash script.sh |
| 两台机器网络通但端口不通 | telnet IP 端口 和 nc -zv IP 端口,检查防火墙策略 |
| 大量CLOSE_WAIT连接堆积 | 检查代码中是否忘记关闭socket连接 |
| 大量TIME_WAIT连接堆积 | 查看ss -s确认,必要时调net.ipv4.tcp_tw_reuse和tcp_fin_timeout |
8.2 独家避坑技巧:使用uname和/etc/os-release确认系统环境
很多命令在不同系统上行为有差异。比如grep的-r参数在GNU grep和BSD grep上的行为基本相同,但sed -i的差异前面提过就很大。所以拿到一台陌生服务器,第一件事不是急着敲命令,而是确认系统版本:
bash复制cat /etc/os-release
uname -a
/etc/os-release告诉你发行版名称和版本号,uname -a告诉你内核版本和CPU架构。这两个输出决定了你后面用什么包管理器、装什么版本的软件、甚至部分命令参数能不能用。这套“环境感知”习惯帮你避开80%的“为什么我的命令跟你的不一样”问题。
8.3 批量操作的细节:循环里怎么处理空格和特殊字符
批量处理文件时,很多人喜欢写for循环:
bash复制for f in $(find /data -name "*.log"); do echo "$f"; done
这个写法有隐患:如果文件名里有空格,$(find ...)的输出会被切成多个词,循环就乱了。更稳的写法是:
bash复制find /data -name "*.log" -print0 | while IFS= read -r -d '' f; do
echo "$f"
done
-print0和-d ''配合,用空字符分隔文件名,IFS=确保不按空格切分,-r防止反斜杠转义。这套组合写出来略显繁琐,但胜在安全,生产环境跑批量处理的脚本时建议直接套用这个模板。
另一个细节是shell脚本里变量一定要加双引号,比如rm -f "$f"而不是rm -f $f。不加引号时,如果文件路径含空格,rm会把它当成多个参数,轻则删错文件,重则误删重要目录,属于必须养成的编码习惯。
8.4 从命令到脚本:如何把常用操作沉淀成自己的工具箱
最后建议各位不要把常用命令停留在“手敲”层面,多花点时间把组合命令沉淀成脚本函数。比如在~/.bashrc里定义一些高频函数:
bash复制# 快速查看端口占用
function port() {
ss -lntp | grep -E "LISTEN.*:$1\b"
}
# 查看当前目录下最大的10个文件
function bigfile() {
du -ah . 2>/dev/null | sort -rh | head -10
}
# 打包并排除指定目录
function tarball() {
tar czvf "$1.tar.gz" --exclude="$1/logs" --exclude="$1/cache" "$1"
}
这些自定义脚本是真正属于你自己的“知识库”,能让你从重复劳动中解放出来。也别怕记不住命令,把本章提到的常用组合存成备忘文件,用的时候查一下,比死记硬背高效得多。用得多了,自然就留在手上了。
我个人在实际操作中最深的体会是,Linux命令这种东西,光看是看不会的,必须遇到真实问题去敲、去排查、去踩坑,才能转化为自己的技能。这篇汇总主要是帮大家把高频场景串起来,真正用时最好能对照着敲一遍。实在记不住也没关系,收藏起来当速查手册用即可。碰到什么好用的命令组合,也欢迎在评论区和大家交流。
