
老有人问我“Linux高级命令”怎么学是不是把命令大全背下来就算高级了。我接触Linux十几年见过太多把几百个命令背得滚瓜烂熟、真到排查问题时却手足无措的情况。所谓“高级命令”真正拉开差距的不是你认识多少条命令而是你用什么思维去组合命令、在什么场景下选对工具、以及遇到问题时能不能把命令用到刀刃上。这篇文章我不会给你堆一份几百条的命令抄写表而是挑几类最吃“内功”的方向——文本处理、权限模型、进程排查、网络诊断、文件系统、内核机制从实战场景出发讲清楚背后的原理和排查思路。适合已经会基本增删改查、想往资深运维或后端方向进阶的人。看完你会发现高级命令不是“冷门命令”而是把常用命令用出不常见的深度。1. 先忘掉“背命令”高级命令的核心是组合思维和“一切皆文件”1.1 一条管道胜过一百行脚本前阵子有个同事给我看他写的日志统计脚本二十多行Python循环读文件、判断状态码、累加计数最后打印结果。我扫了一眼问他为什么不用一条awk加sort就搞定。他愣了一下然后我当着他面敲了这么一行awk {print $9} access.log | sort | uniq -c | sort -rn这是一行不是一页。执行结果直接把每个HTTP状态码的出现次数按从高到低排列得明明白白。他那个Python脚本功能上没错但论排查问题的速度、临时分析日志的效率、以及可维护性这一行管道完胜。这就是我想说的第一层“高级”——理解Linux命令的哲学每个命令只干一件事而且干得极其纯粹然后通过管道把多个命令串起来形成一条流水线。你不需要为了统计日志专门写程序因为grep、awk、sort、uniq这些命令本身就是为你准备好的“零件”。1.2 一切皆文件理解输入输出的统一抽象Linux有一个贯穿始终的设计理念一切皆文件。普通文件是文件目录是文件设备是文件甚至进程的输入输出、网络连接也被抽象成了文件描述符。这不仅是哲学层面的一句话它直接影响你使用命令的方式。比如你想看某个进程在系统调用层面做了什么可以用strace去跟踪你想看一个进程打开了哪些文件可以用lsof去列出来。这些工具的底层逻辑都是围绕“文件描述符”在做文章。再比如你在shell里写的、、21这些重定向符号本质就是把文件描述符从终端指向了别的文件。理解了这一点你就知道为什么/dev/null可以“吞”掉所有输出为什么21要把标准错误和标准输出合并到一起——它们只不过是把不同的“文件流”重新接线罢了。高级命令的操作对象从来不只是“文件”而是“流”和“文件描述符”。1.3 组合思维的几个经典范例举几个我工作中高频使用的组合例子# 找出当前目录下占用空间最大的10个文件 du -ah . | sort -rh | head -10 # 统计nginx日志里访问量Top 10的IP awk {print $1} access.log | sort | uniq -c | sort -rn | head -10 # 找出所有80端口监听进程的PID和命令名 ss -tlnp | grep :80 | awk {print $NF} | tr -d pid | sort -u这些命令单拆开看没有一个是“高级命令”但组合起来它们的表达力和效率远超你临时写脚本。这才是高级命令的精髓你不需要认识更多命令你需要更懂你已经认识的那些。2. 文本处理三件套把grep、sed、awk用到“出神入化”2.1 grep不只是搜索更是上下文挖掘很多初学者用grep只会grep error file.log。够不够用小日志够生产环境根本不够。我实际排查问题时最常用的是下面这几个姿势# 输出匹配行的同时显示前后5行上下文定位问题前后发生了什么 grep -C 5 ERROR app.log # 只显示匹配行前面的内容比如看某次操作发生前用户做了什么 grep -B 10 timeout app.log # 只显示匹配行后面的内容比如看报错后系统输出了什么堆栈 grep -A 20 Exception: app.log # 使用扩展正则一次匹配多种模式 grep -E ERROR|FATAL|NullPointer app.log # 递归搜索代码目录排除无关目录 grep -rn --include*.java --exclude-dirtarget searchText .真正的老手还懂得用-P启用PCREPerl兼容正则配合-o只输出匹配部分直接提取日志中的IP、邮箱、订单号。比如从一大段日志里把所有的订单编号抠出来grep -oP order_[0-9]{8}_[A-Z]{2} app.log | sort | uniq -c这一条命令在数据提取场景里效率比手工复制粘贴高几个量级。另外grep的-v反向匹配非常实用比如过滤掉日志里的调试噪音grep -v DEBUG app.log | grep WARN|ERROR2.2 sed流编辑器的三个高频场景sed全名叫stream editor它不打开文件而是像流水线一样一行一行读入、处理、输出。我常用在三个场景第一批量替换文件内容。注意sed s/old/new/g file只是在屏幕上输出替换后的结果并不写回文件。真要把改动落盘用-i参数。这里有个大坑-i在GNU sed和BSD sedmacOS自带上语法不一样一个是sed -i s/old/new/g file另一个是sed -i s/old/new/g file。在Linux服务器上写习惯了跑到mac上顺手一条sed -i就会报错。建议在Linux服务器上做文件批量替换时先不带-i跑一遍看输出确认无误再加-i。第二按行范围操作。比如你只想看脚本第20到30行或者想把第5行删除sed -n 20,30p script.sh # 打印第20到30行 sed -i 5d script.sh # 删除第5行并写回文件第三利用sed做多行合并。在格式化某些输出时特别有用比如把每两行合并成一行sed N;s/\n/ / pairwise.txt2.3 awk文本处理里的“编程语言”awk比sed更进一步它有字段分割、变量、条件判断和循环本质上是一门小型的文本处理语言。它的核心思维是“按行读入、按字段处理”。默认按空白字符分割字段$1、$2分别对应第一列、第二列$0代表整行。我最早被awk惊艳到是处理下面这种场景看某台机器上内存占用最高的几个进程并直接输出进程名和占用内存ps aux | awk $4 5.0 {print $11, $4%}这条命令解释一下ps aux输出的第四列是内存占用百分比awk判断如果某行第四列数值超过5.0就打印该行的第11列进程名和第四列本身。一条命令完成“过滤筛选格式化输出”三个动作。awk还支持BEGIN和END块。BEGIN在读取任何输入前执行通常用来设置变量或打印表头END在所有行处理完后执行通常用来输出汇总结果。比如统计日志文件总行数、总流量字节数awk {total $10} END {print total bytes:, total} access.log这里$10是nginx日志里的“发送字节数”字段awk逐行累加最后在END块里输出总和。你不需要写循环、不需要定义数组当然awk也支持一行搞定。再升级一下awk还能处理多文件、做字符串分割、定义函数。很多高级运维的“高光时刻”都是一条awk命令直接解决了一个看似要写脚本的问题。我的建议是初学者至少把字段处理、BEGIN/END、printf格式化输出这几个点吃透就足够覆盖90%的日常场景了。3. 权限模型不止chmodsetuid、ACL、文件属性与capabilities3.1 为什么普通用户也能改密码setuid的魔力Linux的权限体系很多人停留在“所有者、所属组、其他人”三组rwx权限上。但有一个问题是新手很容易困惑的/etc/shadow文件保存着所有用户的密码哈希权限是root才能读写那普通用户运行passwd命令时系统是怎么把新密码写进这个文件的答案就是setuid位。看下面这个ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 59976 Feb 6 2024 /usr/bin/passwd注意所有者权限位里那个s。这个s表示setuid意思是当普通用户执行这个程序时进程的有效用户ID会被临时提升为文件所有者root于是它就拥有了写/etc/shadow的权限但在完成密码修改后会迅速释放这个权限。系统通过这种非常克制的“瞬间提权”机制实现了“普通用户可以改自己密码、但不能把整个系统搞翻”的平衡。这也给了我们一个安全运维的重要提醒在你自己写脚本或程序时绝对不要轻易给可执行文件加setuid位。一个setuid root的脚本如果有漏洞就是给攻击者免费开了一道root权限后门。我见过有团队为了省事给一个备份脚本加了setuid结果脚本里有一处路径拼接没有做限制被搞出了本地提权这是非常昂贵的教训。3.2 ACL传统九位权限不够时的补丁传统权限只有“所有者/组/其他”三组可现实往往是这个文件要给A用户读、给B用户写、给C组执行三方权限各不相同。传统chmod根本表达不了这种细粒度策略。这时候用ACL访问控制列表。# 给特定用户单独授予读写权限 setfacl -m u:zhangsan:rw /data/project/config.yaml # 给特定用户组授予读权限 setfacl -m g:devops:r /data/project/config.yaml # 查看文件上的ACL规则 getfacl /data/project/config.yaml # 删除某个用户的所有ACL setfacl -x u:zhangsan /data/project/config.yaml设置了ACL后ls -l会在权限位的末尾显示一个提醒你“这文件不止传统权限还有扩展ACL”。ACL在共享存储、项目组协作还有CI/CD构建机上的“多个角色共享同一目录”场景里极为实用。需要注意的是文件设置了ACL之后传统chmod对“组权限位”的修改会和ACL mask产生联动具体规则有点绕但记住一个结论就行别混着用要么走ACL、要么走传统chmod混用容易把自己绕晕。3.3 文件属性chattr i 的防删防改有一类“权限”不在chmod体系里而是文件系统层面的属性attribute。最常用的是chattr i它让文件变成不可变immutable即使是root也不能删除、改名、写入或创建硬链接。想改回来得先chattr -i。这个功能在加固关键文件时非常有用chattr i /etc/passwd /etc/shadow /etc/sudoers还有一个chattr a只能追加、不能覆盖和删除适合写审计日志的场景。加上了这些属性的文件ls -l 看不出来要用lsattr查看。我遇到过一台被搞过的机器攻击者把/etc/ld.so.preload加了i属性结果管理员想删掉这个恶意文件直接rm删不掉必须先用chattr -i解除——这种细节在应急响应时能救急。3.4 capabilities更精细的“免root”授权传统的setuid是“全有或全无”要么普通用户权限要么直接变成root权限粒度太粗。现代Linux内核引入了capabilities机制把root的超级权限拆成了几十种独立的小权限比如CAP_NET_BIND_SERVICE允许绑定1024以下端口、CAP_DAC_READ_SEARCH绕过文件读权限检查、CAP_SYS_ADMIN挂载文件系统等。这意味着你可以让某个程序只拥有“绑定80端口”的权利而不用把整个root权限交给它# 允许某个可执行文件绑定1024以下端口 setcap cap_net_bind_serviceep /usr/local/bin/myapp # 查看程序的capabilities getcap /usr/local/bin/myapp在生产环境里我经常用这种方式代替“给程序加setuid”或“用root跑服务”可以把服务被攻破后的影响范围控制到最小。从运维安全的角度说这是一个值得养成的习惯能用capabilities解决的需求不要给setuid更不要直接给root。4. 进程排查的完整链路从ps、top到lsof、strace层层深入4.1 用ps和top先锁定“嫌疑对象”进程排查是我日常工作里最高频的场景。服务器负载突然飙高、CPU跑满、内存告警这些问题的排查路径其实有固定的套路。第一步用top看一眼全局按CPU占用排序进入top后按P键、按内存占用排序按M键。top显示的是动态刷新的实时状态适合快速感知“哪几个进程在吃资源”。第二步用ps拿到固定输出方便继续操作和留档。这里推荐这个组合ps -eo pid,ppid,user,%cpu,%mem,stat,etime,cmd --sort-%cpu | head -20解释一下-e表示显示所有进程-o自定义输出列--sort-%cpu按CPU占用从高到低排列。我自己习惯把这个定义成一个别名排查问题的时候直接敲。这里的STAT列值得关注R表示正在运行D表示不可中断睡眠通常是等待IO如果大量进程处于D状态大概率是磁盘出问题了Z是僵尸进程。4.2 lsof拿着“找文件”的思路找进程和端口之前说“一切皆文件”lsoflist open files就是把这句话变成实战工具的代表。它的功能非常多但最常用的其实是三个方向# 1. 查看某个进程打开了哪些文件 lsof -p 1234 # 2. 查看某个端口被谁占用这一步在排查“端口被占”时必用 lsof -i :8080 # 3. 查看某个文件正被哪些进程使用比如想umount一个目录却提示busy lsof /data/logs/app.log尤其是第三个方向我在迁移存储、卸载磁盘时几乎必用。系统提示target is busy你第一反应别去强改fstab先lsof D /data看看是哪个进程还拽着这个目录不放找到后正常停掉进程问题自然解决。4.3 strace当一切表象都查不出来时直接看系统调用有时候进程状态很诡异CPU占用不高、内存正常、日志也没报错但服务就是没响应。这种时候常规排查手段基本失效就得请出strace。它的原理是跟踪进程发出的每一个系统调用和收到的每一个信号相当于给进程装了一个“对外动作记录仪”。最常见的用法# 跟踪某个进程的系统调用 strace -p 1234 # 跟踪某个命令的完整执行过程常用于命令启动失败时 strace -f -o /tmp/syscall.log ./start.sh # 只看网络相关的系统调用 strace -e tracenetwork -p 1234-f表示同时跟踪子进程-o把输出写进文件。我遇到过一个真实案例某个服务启动时提示缺少某个配置文件但配置明明是存在的。用strace -f -o /tmp/start.log ./start.sh翻开日志发现它在启动过程中用相对路径去找配置文件而工作目录被systemd服务定义里的WorkingDirectory指到了一个完全不同的路径。这种问题不加strace查半天加了以后一分钟定位。还有一个实战技巧想提高性能时用strace看某个进程最频繁的系统调用是什么往往能直接找到性能瓶颈。strace -c -p 1234跑几分钟后按CtrlC它会统计出每个系统调用的次数和耗时占比哪项耗时最高瓶颈就在哪。5. 网络诊断工具链ss、iperf3和tcpdump的合理分工5.1 ss看连接状态netstat的现代替代品早期习惯用netstat的朋友建议尽快切到ss。它直接从内核读取socket信息输出更快、信息更全而且参数语义和netstat类似迁移成本极低。# 查看所有TCP连接及对应的进程 ss -tpn # 看Listen状态的端口相当于以前的 netstat -lntp ss -tlnp # 按状态统计TCP连接数排查连接数过多时很直观 ss -s # 筛选出所有TIME_WAIT状态的连接 ss -tan state time-wait排查“连接数堆积在TIME_WAIT”是后端服务常见的问题。ss -s一眼就能看到当前系统的TIME_WAIT总数。如果数量奇高说明服务端主动关闭连接的频率过高这往往和HTTP keep-alive没生效或者负载均衡的健康检查过于频繁有关。看到数据以后再针对性地去调net.ipv4.tcp_tw_reuse或改造连接复用而不是凭感觉乱调内核参数。5.2 iperf3测带宽别再用scp去“瞎试”经常有人问怎么测两台服务器之间的真实网络带宽有人直接scp一个大文件看速度这个方法受磁盘性能、文件系统缓存、协议开销的影响太大测出来的数字很不“纯净”。专业做法是使用iperf3。部署非常简单两端各装一下# 服务端接收流量的一端 iperf3 -s # 客户端发送流量的一端测10秒TCP带宽 iperf3 -c 192.168.1.100 -t 10 # 反向测让服务器端向客户端发流量排查单向上行/下行带宽不对称 iperf3 -c 192.168.1.100 -R -t 10 # 同时开8个并发流看多流并发时的总带宽 iperf3 -c 192.168.1.100 -P 8 -t 10这里有几个细节容易踩坑。一是防火墙iperf3服务端默认监听5201端口记得在防火墙规则里放行不然客户端一直报连接超时。二是带宽瓶颈判断如果单流带宽上不去但多流-P 8总带宽能上去说明瓶颈大概率在单TCP流的窗口或应用层而不是链路本身。三是打流方向默认是客户端往服务端方向发流量容易忽略接收方向所以排查时一定要正向、反向都测一遍。很多网络“频繁超时但Ping不丢包”的诡异问题通过iperf3双向打流一下子就暴露出来了——要么是某一侧网卡协商速率不对要么是交换机端口限速。5.3 tcpdump抓包看到“数据到底走没走”ss看状态、iperf3测速率但如果要确认具体某个请求的数据包内容、重传情况、握手过程就得靠tcpdump抓包。# 抓取某网卡、指定端口的流量保存到文件 tcpdump -i eth0 -nn port 8080 -w /tmp/capture.pcap # 实时查看某台客户端与本地服务的TCP握手包 tcpdump -i eth0 -nn host 192.168.1.50 and tcp port 8080抓到的包可以用Wireshark打开分析也可以直接用tcpdump配合-A或-X看ASCII内容。我印象最深的一次排查是服务端日志里明明显示收到了客户端请求但客户端一直报超时。抓包一看服务端回给客户端的SYN-ACK包用户态根本没收到因为中间有个安全设备在丢包。这种问题不看包靠猜是猜不出来的。现在很多场景其实可以先用ss -tlnp看端口监听再用ss -tan state established看已有连接最后才上tcpdump。三步走每一步都有不可替代的价值。6. 文件系统层面的“隐含知识”inode、硬软链接和已删除文件6.1 inode与链接面试题的背后是真实场景“什么是硬链接和软链接”是Linux面试高频题但很多人只会背答案不知道这背后的真实应用场景。硬链接共享同一个inode号文件数据本身只有一份只是目录里有多个名字指向它。软链接符号链接有自己的inode它保存的是另一个文件的路径字符串就像Windows的快捷方式。硬链接最常见的实战价值是“防止重要文件被误删”。比如我要维护一个关键的系统脚本我先给脚本建立一个硬链接ln /opt/scripts/backup.sh /opt/safe/backup_hardlink.sh以后误删了/opt/scripts/backup.sh只要ls -l时链接数还没有降到0数据就还在可以通过硬链接找回。但是注意硬链接不能跨文件系统不能对目录创建硬链接这也是为什么目录链接数不是1而是2里面每个子目录都会让父目录的链接数1。而软链接则可以用在跨文件系统、指向目录、指向不存在的目标等更灵活的场景。判断一个目录下哪些文件是硬链接用ls -li看第一列的inode号是否重复。生产环境迁移时如果发现inode号相同说明是硬链接关系复制时要小心不要重复复制内容。6.2 df和du的结果对不上八成是有“已删除但还被占用”的文件另一个高频坑明明把一个大文件删了但df -h显示磁盘空间还是满的。这是因为有进程仍然持有这个文件的文件描述符文件数据并没有真正释放。查看方法非常经典lsof | grep deleted看到的结果里会有类似(deleted)的标记记下对应的PID重启或正常停掉这个进程后磁盘空间才会真正释放。这个坑在日志文件场景里尤其常见很多服务把日志文件打开后就一直持有你用rm access.log删除进程还在不断往里写磁盘空间不降反升就是因为删掉的只是目录项文件数据一直没释放。另外还有一个和inode相关的坑df -i显示inode耗尽。这种情况通常是某个目录下产生了海量小文件比如邮件队列、临时文件目录成千上万的小文件把inode号用光了但磁盘块还没被打满。排查方法就是df -ih看哪个文件系统inode满了进去找小文件目录清理后即可恢复。7. 内核层面的“高级玩法”读懂file_operations与动态加载的拦截逻辑7.1 file_operations是什么进入到内核层面很多非内核开发的人会觉得这是“另一片天地”。但实际上你天天在用Linux你输入命令、读写文件、访问设备这一切最终都会和file_operations结构体打交道。它是Linux VFS虚拟文件系统的核心接口一个结构体里面放着一堆函数指针分别对应read、write、open、release、ioctl等文件操作。也就是说每次你在应用层调用read读一个文件内核最终都会找到这个文件对应的file_operations里的read函数来执行实际读取逻辑。struct file_operations { ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); int (*open)(struct inode *, struct file *); int (*release)(struct inode *, struct file *); ... };普通文件、字符设备、块设备每种文件类型都维护着自己的file_operations。如果这个结构体里的read/write被“做手脚”那所有通过VFS层读写的文件数据都会被“过一遍手”——这正是很多内核安全产品、透明加密系统、审计监控工具的工作原理。7.2 动态加载内核模块的流程内核开发里有一个概念叫“内核模块”loadable kernel module允许在不重新编译整个内核的情况下把功能代码以.ko文件的形式动态加载到正在运行的内核里。动态加载本身就很有魅力部署新特性、修bug、做监控都不需要重启系统。基本流程是# 编译生成内核模块文件 make # 加载模块 insmod mymodule.ko # 查看已加载的模块 lsmod | grep mymodule # 卸载模块 rmmod mymodule模块加载时会调用模块里实现的module_init指定的初始化函数卸载时调用module_exit指定的清理函数。要“替换”某个文件的read/write行为常见思路是在初始化函数里保存原始的file_operations然后把目标文件的操作函数指针替换成自己实现的拦截函数卸载时再恢复回去。7.3 拦截read/write的正规业务场景直接给一段修改文件操作的代码示例不太合适容易让不熟悉内核开发的人产生误解以为“改内核很简单”。但我要强调一点这种技术本身大量存在于合法的企业级安全产品中典型场景包括透明加密系统应用层无感读取加密文件时内核拦截read自动解密写入时自动加密落盘。数据防泄漏DLP系统监控特定文件的读写操作识别敏感数据外泄。文件审计与合规监控记录某个关键目录下所有文件的读写行为用于事后追溯。服务器入侵检测HIDS/Rookit检测的内核态组件对比和分析内核函数调用链。我参与过的某个数据安全项目里就有一个专门的内核模块负责拦截指定目录下文件的write操作把写入的数据同步计算哈希并记录写入进程的PID、时间戳。这个模块的价值在于它不依赖应用层是否“配合”因为拦截点在内核VFS层应用层任何绕过手段都绕不开文件读写本身。这类能力是纯用户态轮询完全做不到的。当然把内核模块写成什么样一定要经过严格的代码评审和故障演练。内核模块一旦有bug轻则功能失效重则panic这是我在实际项目中反复强调的红线。**任何内核态的修改都必须先在测试环境完整验证并准备好快速卸载和回滚方案。**不要把生产环境当成内核试验场。回到普通使用者的视角理解file_operations的意义在于当你再遇到某个监控类软件“为什么连vim写文件都能拦到”“为什么用户态改了文件属性也逃不掉审计”这类问题时你至少能想明白这是在内核层做了一层透明的钩子。这种对整个系统“自底向上”的理解才是Linux高级能力真正的分水岭。写在最后的一个小经验如果非要总结一条“通往高级命令”的路径我的体会是别去追求冷门命令的数量而是把高频命令的细节琢磨透。你真正需要练的是面对一个具体问题时的“拆解能力”——先拆出问题的本质是文件、是进程、是网络、还是权限再选最合适的工具组合去验证和定位。每解决一个真实故障你对这些命令的理解就会深一层。这种积累没有捷径但一旦形成很多看起来“很高级”的操作对你来说就只是顺手而已。