
1. 先搞懂命令组合的核心逻辑1.1 命令不是一门“背术语”的学问Linux 命令看起来又多又杂但实际上日常工作里高频用到的也就那么几十条。我见过很多朋友把精力花在背“命令大全”上今天背明天忘最后真正上手干活的时候还是抓瞎。这里最关键的问题不是记性不好而是没有先弄懂 Linux 命令背后的组合逻辑。Linux 的设计哲学很朴素每个命令只做好一件事然后再通过管道、重定向、变量这些“连接件”把简单命令组装成复杂能力。比如我要统计一份访问日志里哪个 IP 出现的次数最多不需要写一段 Python只需要把cat、awk、sort、uniq、sort、head用管道串起来一行就能出结果。这个思路才是“无废话”的核心命令本身不复杂复杂的是组合方式。1.2 管道和重定向真正的高频战场管道符号|的作用是把左边命令的标准输出接到右边命令的标准输入。重定向就是把命令的输出从屏幕改到文件里覆盖写入追加写入2重定向错误输出。举个实战例子。我经常需要从一个日志文件里把带 ERROR 的行都拎出来同时把错误输出丢弃grep ERROR app.log 2/dev/null | wc -l这条命令的意思是在 app.log 里找 ERROR如果文件不存在之类的错误信息就不打扰我然后把结果的行数统计出来。2/dev/null这个写法非常常用因为很多命令在正常输出之外还会往标准错误里吐一堆警告干活的时候这些警告大多是噪音。还有一个容易忽略的操作命令替换。用反引号或者$(...)可以把一个命令的输出直接当作另一个命令的参数。比如我经常要停掉一个进程但进程 PID 每次都会变那就直接写成kill -9 $(pgrep -f my_service)先执行pgrep -f my_service拿 PID再传给kill。写脚本的时候$(...)几乎天天用这个比反引号好读也好嵌套。注意管道传递的是标准输出不是标准错误。如果命令报错信息也想往下传得先21把标准错误合并到标准输出或者用|bash 里支持。这条看起来是小细节但排查问题的时候经常就是因为少写了它导致日志里真正有用的报错没被管道处理到。1.3 命令不要靠背要靠拆解遇到一个没见过的命令很少有人真靠 man 手册硬啃完。我的习惯是直接跑一下看输出然后拆解它的参数。比如说ls -l会输出一堆字段我会把这几个字段对应到“权限、链接数、所有者、组、大小、时间、文件名”自然就理解了。更重要的是养成一个习惯看到一个命令组合先问自己“它前一步输出了什么后一步吃了什么”。比如ps aux | grep nginxps aux输出的是所有进程的完整列表grep nginx从里面筛出包含 nginx 的行。理解了这个你就知道grep -v是反向过滤掉 nginx 行而不是“只显示 nginx 之外的东西再反向一次”。Linux 给人感觉很“硬核”其实是因为它的命令不做多余解释默认全沉默。真正动手敲一遍出错、排查、修参数比看十页文档都好使。无废话的实质是让每条命令都直接服务于手头的问题。2. 文件操作高频指令别只会 ls 和 cd2.1 ls、find、touch 这类“小命令”的细节很多人用ls就是看文件名但ls的几个参数在干活时非常实用。ls -l看权限和大小ls -a看隐藏文件ls -lt按修改时间从新到旧排序——这个我几乎天天用因为最近改动的文件往往就是问题所在。ls -lh里的-h会把大小显示成 K/M/G人眼一下子就能看出哪个文件是“大块头”。find是我见过学习曲线最友好的也是踩坑最多的。最基本的用法是find 路径 -name *.log注意这里-name支持通配符但要用引号包起来不然 shell 可能先把*.log展开了结果和想象的不一样。按时间找文件是很高频的需求。我常用的是find /var/log -name *.log -mtime -7意思是找/var/log下七天之内修改过的 log 文件。排查问题的时候这种方式比一个个目录翻高效得多。如果想按大小找大文件find / -type f -size 500M这个在定位“哪来的大文件把磁盘塞满了”的时候非常直接。注意/全盘搜索会比较慢建议先圈定到/home、/var这种候选目录。touch除了新建空文件还有一个常见用途修改时间戳。有些程序会判断配置文件是否比缓存文件新调试的时候我就靠touch config.yml强制刷新时间戳让它重新加载配置。实操心得find删除文件用-delete要非常小心。我见过有人写find . -name *.tmp -delete本来只想删临时文件结果因为路径写错删了一堆不该删的。稳妥做法是先不加-delete跑一遍find确认列出来的文件没有误伤再加-delete执行。多花十秒避免一次灾难。2.2 cp、mv、rm三步防误操作这三个命令本身太简单了所以很多人根本不防护。但实际上文件误删、覆盖基本都栽在这几个命令上。日常写脚本我强烈建议给cp、mv、rm养成加-i的习惯。-i的意思是覆盖或删除前询问确认虽然多敲一下回车有点烦但关键时刻能救命。cp复制目录必须加-r这个坑新手必踩cp -r /data/backup /data/backup_2025批量删除的时候rm还有一个坑默认不会递归删除目录。想删干净目录要用rm -rf但-rf加上了就要对路径高度警惕。我从不敢在脚本里写类似rm -rf /var/tmp/${APP}这种变量路径因为一旦变量没设置成空结果就是灾难。更稳妥的做法是先用cd进到目标目录再rm -rf ./*把风险圈定在已知范围。mv的常用场景之一是把旧配置文件备份成带时间的名字mv app.conf app.conf.bak.$(date %Y%m%d)这样既保住了旧文件又不会污染新配置。2.3 tar 与 rsync备份和同步的实战写法压缩解压是备份、部署绕不开的操作。我保存文件的习惯是能少敲字符就少敲所以tar命令我几乎不用写压缩格式的参数了tar -czf app.tar.gz ./app tar -xzf app.tar.gz-z对应 gzip-c是创建-x是解压-f指定文件名。老版本看后缀自动识别容量有限但常见的-z和-j对应 bzip2记住就够用了。解压的时候我喜欢加-C指定目录避免把一堆文件直接撒在当前位置tar -xzf app.tar.gz -C /opt/apprsync才是同步和增量备份的王牌。它的特点是“只传变化的文件”尤其适合大量小文件或每次只改动一点点的场景。我最常用的是rsync -avz --delete /data/app/ userserver:/data/app/这里-a是归档模式保留权限、时间等元信息-v显示过程-z传输时压缩--delete会把目标端多出来的文件删除让两边严格一致。这个--delete同样要小心第一次用可以先不加跑一次确认没删错再加。如果只想排除某些目录用--excludecache/rsync -avz --exclude*.log /data/app/ /backup/app/实操心得rsync的结尾斜杠是个大坑。rsync /data/app userserver:/data/app和rsync /data/app/ userserver:/data/app/的行为不一样——加斜杠表示“把这个目录里的内容”不加斜杠表示“把这个目录本身”。我见过很多同步完目录层级错乱的情况基本都出在这个斜杠上。建议固定写法源和目标都加斜杠表示同步目录内容层级最不容易乱。3. 文本处理与查找三剑客的实战用法3.1 grep除了搜索还能做这些grep是日志排查里出现频率最高的命令。基础用法grep 关键字 file就不多说了几个进阶场景更值得掌握。日志里查某个时间段内的报错我先用grep -n看行号确认错误大概在文件哪个位置grep -n ERROR app.log查上下文用-A 3和-B 3分别表示显示匹配行后面 3 行和前面 3 行。看报错的时候单看一行往往不知道上下文发生了什么带上上下文才能快速还原现场grep -A 5 -B 5 OutOfMemory app.log如果要在多个文件里搜-r递归进去加-l只列出文件名加-c统计每个文件匹配行数。比如想知道哪个日志文件里报错最多grep -rc ERROR /var/log/app/ | sort -t: -k2 -rn另外grep默认支持的是基础正则。有些字符像、(、)在基础正则里是“普通字符”想用扩展正则需要grep -E。比如匹配abc出现 1 次或多次grep -E abc app.log还有一个我自己常用的匹配多关键字中的任意一个语法是grep -E A|B这个比写两条 grep 再合并快得多。注意很多人用ps aux | grep nginx但这条命令会把自己的 grep 进程也带出来因为命令行里包含“nginx”字样。不想看到它的话可以ps aux | grep [n]ginx用括号技巧让 grep 不匹配自己。3.2 sed最常用的替换和裁剪sed的定位是“流编辑器”不需要一次性把整个文件放进内存一行一行处理所以对大文件特别友好。日常用得最多的场景是文本替换sed -i s/old_string/new_string/g config.conf-i表示原地修改s是替换命令g是全局替换每一行所有匹配。注意不加g的话每行只替换第一个匹配结果这是个非常容易踩的细节。我经常用sed做配置文件批量更新比如把开发环境的 IP 改成测试环境的 IPsed -i s/192\.168\.1\.100/192.168.1.200/g .env正则可以写复杂一点但日常替换里点号.是个坑因为它匹配任意字符。上面我写成192\.168\.1\.100就是先把点号转义了否则192x168x1x100也会被匹配到。sed的另一个高频用法是“打印指定范围的行”。用-n配合地址范围比如打印文件的第 100 到 200 行sed -n 100,200p app.log配合grep的逻辑我经常这样快速切片日志先grep -n找到关键字行号再sed -n 范围p把整段拉出来。比如关键错误出现在 12345 行我想看从 12300 行到 12400 行sed -n 12300,12400p app.log这样比一次性把文件全读出来高效得多。3.3 awk列处理利器awk是文本处理里最能提效的命令。它的设计理念是把每一行按分隔符切成列然后对这列做操作。默认分隔符是空白符比如日志里2025-01-01 12:00:00 INFO request ok用awk {print $1, $2, $3}就能分别拿到日期、时间、级别。最经典的价值案例是处理日志统计。看一个日志里访问最多的 IPawk {print $1} access.log | sort | uniq -c | sort -rn | head -20先awk取第一列 IP再sort排序uniq -c统计每个重复值的次数又sort -rn按次数降序最后head取前 20。这一串命令我写了无数遍已经是肌肉记忆了。awk还可以做条件过滤比如只关心状态码不是 200 的请求awk $9 ! 200 {print $1, $9, $7} access.log列号对应具体哪一列得看你日志的格式但基本套路都一样想筛哪列就按列序号取哪列。awk还有两个内置变量很常用NF表示当前行有多少列NR表示处理到第几行。比如只看日志前 100 行里的后两列awk NR 100 {print $(NF-1), $NF} app.log这种列裁剪能力是grep和sed做不到的三者配合起来才叫文本三剑客。3.4 sort 与 uniq统计组合单独用sort只是排序单独用uniq只能去掉相邻的重复行可这俩组合起来就能做统计。前面的sort | uniq -c | sort -rn就是一个固定套路我建议把它当成一个“模板”来记cat file | sort | uniq -c | sort -rnuniq -c会在每行前面加上出现次数sort -rn的-n是按数字排序-r是倒序。统计一个日志里各类状态码数量awk {print $9} access.log | sort | uniq -c | sort -rn如果要按某个字段去重比如想看会话里有哪些用户出现了sort -u比uniq更省事因为它直接同时完成排序和去重awk {print $2} app.log | sort -u实操心得日志统计最容易翻车的地方是“列号不对”。不同日志格式比如自带时间戳额外列或者字段长短不一致会导致$1、$9对应错位。我每次拿到新日志都先head -3 access.log看下格式再写 awk这比直接猜列号稳得多。4. 权限、进程与网络排查问题三板斧4.1 权限看懂 ls -l 和权限设置ls -l输出里第一段十个字符比如-rwxr-xr--含义是第一个字符是文件类型-普通文件、d目录、l链接后面九个字符分成三组分别对应所有者、所在组、其他用户的读r、写w、执行x权限。权限的数字表示法记忆起来很简单读是 4写是 2执行是 1。所有者 4217组 541其他人 4那么chmod 754 script.sh就是把所有者设为读写执行、组设为读和执行、其他人设为只读。给脚本加执行权限是新手最容易忘的一步chmod x script.sh ./script.sh另外我建议把所有者和组的概念理清楚chown user:group file可以同时改所有者和组。部署应用的时候经常会遇到“文件是 root 的但 nginx 进程用别的用户起读不了文件”这种问题排查思路就是先看进程是以哪个用户跑的再对应改权限或属主。注意目录的“执行权限”不是执行程序而是“能否进入这个目录”。所以目录一般是755文件一般是644。用find批量调权限的时候别一刀切注意区分文件和目录。4.2 进程ps、top、kill我现在看进程列表基本不用ps aux之外的方式了。ps aux输出全量进程字段包括 CPU、内存占用、运行用户、启动时间、命令。想知道哪个进程在跑直接ps aux | grep java想知道谁占 CPU 最多可以用top然后按P键按 CPU 排序按M键按内存排序。如果想要一键看结果而不是交互界面可以top -bn1 | head -20-b是批处理模式-n1是只刷新一次然后截取头部 20 行适合脚本里取快照。杀掉进程是我见过用法最混乱的。kill默认发的是 TERM 信号让进程“优雅退出”给它自己处理收尾工作。kill -9是强制终止进程没机会清理现场可能导致临时文件残留、数据没刷盘。我自己的标准是先用kill PID试试过几秒再看进程是否还在还在的话再kill -9。有些服务比如数据库进程直接-9非常危险能不用坚决不用。批量杀进程用pkillpkill -f my_service-f表示匹配整个命令行而不只是进程名。这个对用命令行参数区分多个实例的场景特别方便。4.3 网络端口排查套路排查“服务怎么起不来”“端口被谁占了”是每个服务器管理员都会遇到的问题。最快的方式是用ss或者老牌的netstatss -tlnp-t看 TCP-l只看监听端口-n不解析域名-p显示进程信息。一条命令就能看到端口和占用进程。想单独查某个端口ss -tlnp | grep :8080如果端口被别人占了马上就能看到 PID然后顺着 PID 去找哪个进程在听这个端口。测端口通不通我常用ncnc -zv 192.168.1.50 3306-z表示只测试不传数据-v输出详细信息。这一条在排查“数据库连不上”“某个应用发不出请求”的时候特别快比写脚本重试好用多了。另一个高频操作是测试 HTTP 接口响应curl必会。看响应头curl -I https://example.com带请求头访问接口curl -H Authorization: Bearer xxx -X POST -d {key:value} http://localhost:8080/api-X指定方法-d指定请求体内容类型默认是application/x-www-form-urlencoded如果发 JSON 最好再加-H Content-Type: application/json。实操心得曾经有次服务一直重启失败查了半天最后发现是端口没释放——旧进程没退出新进程起不来。经验就是遇到“起不来”先跑一句ss -tlnp | grep 端口号十次里能救九次。这比盯着日志猜原因快得多。5. 磁盘、日志与性能定位卡顿的固定套路5.1 磁盘空间与目录占用磁盘满是最常见的事故没有之一。第一步先看整体使用率df -h-h显示人类可读的大小。如果某个分区使用率到了 90% 以上就继续查到底哪个目录占空间。查目录占用用dudu -h --max-depth1 /var | sort -rh | head -20--max-depth1表示只看本级目录总大小不往下递归展开sort -rh按大小降序。这个命令组合能让你一眼看出/var下哪个子目录最胖。想免费判断当前目录下哪些东西大du -sh * | sort -rh | head -10-s是只汇总单个目录总大小-h可读大小。这个用法是我用得最多的磁盘排查命令。5.2 内存的观察方法很多人一看到内存占用高就紧张其实 Linux 的内存管理和人直觉不太一样。free -h输出里有两个关键数字used和available。used会把文件缓存也算进去所以并不代表真的“不够用”available才是真正可以给应用用的内存估算值。我判断内存是否紧张只看available不看used。如果怀疑某个进程吃内存用top按内存排序或者在ps里直接查最占内存的进程ps aux --sort-%mem | head -10--sort-%mem按内存占用降序排head只看前面十个。5.3 日志查询journalctl 与 tail用 systemd 的机器上journalctl是查系统日志和服务日志的主要方式。我常用的几个组合journalctl -u my-service --since 1 hour ago只看某个服务最近一小时的日志。-u指定服务名--since控制时间范围。加-f是跟随模式实时看输出调试启动过程很舒服journalctl -u my-service -f按优先级筛日志比如只看 ERROR 级别以上journalctl -u my-service -p err --since today传统日志文件下tail是主力。tail -f实时追踪文件末尾tail -n 100看末尾 100 行。我要区分-f和-F-f跟踪的是文件描述符如果日志文件被轮转重命名再新建-F会自动重新打开新文件。找线上日志时建议直接用-F不然日志一滚动就断片了。5.4 一个完整定位流程我把排查问题的固定套路总结一下这样的流程基本覆盖了“服务不对劲”的大多数场景先systemctl status 服务名看服务是否活着没活着看journalctl -u 服务名 --since 30 min ago。存活但响应慢用top看 CPU、内存用ss -tlnp看端口。磁盘告警用df -h加du -sh *找大目录。日志里定位异常grep -A 5 ERROR拉上下文。实在定位不了看系统负载和 IO再用tail -F守着新日志。这套流程的价值在于每一步都有明确的命令和输出不会让人对着黑屏发呆。排查问题不是比谁命令记得多而是比谁能把流程走通。注意生产环境执行journalctl和df是安全的但别对生产环境随手执行rm、kill -9这种有副作用的命令。先确认这批命令只读探查再动手改东西这是一个基本素养。6. 把 Shell 变成工作台的几个习惯6.1 配置别名和补齐高频命令手敲固然没问题但上了年纪、手速没以前快之后我真的会给自己配别名。配置文件是~/.bashrcbash或~/.zshrczsh。我的常用别名alias llls -lht alias lals -a alias ..cd .. alias glgit log --oneline --graph设置完记得source ~/.bashrc让它立刻生效。命令行自动补全也是提效利器bash 按Tab补全命令和文件名连续按两次Tab会列出所有可选项。这个习惯一旦养成输入长路径时基本不会打错。6.2 history 与快捷键history命令查看历史记录但真正快的是CtrlR反向搜索历史命令。输入几个字母就能把之前敲过长命令找回来。例如CtrlR然后输入rsync就会匹配到最近一次带rsync的命令再按一下CtrlR往前翻更早的。方向键上下翻历史也有用但遇到复杂命令我更推荐CtrlR。另外几个日常会用到的快捷键CtrlA跳到行首CtrlE跳到行尾CtrlW删除前一个单词CtrlU清空当前行输入。这些快捷键的意义在于不用操控鼠标也不会打断思路。6.3 脚本调试必知写 Shell 脚本的时候我建议一上来就开两个“安全开关”#!/usr/bin/env bash set -euo pipefailset -e表示只要某条命令出错就退出脚本避免在错误状态下继续运行产生副作用set -u表示使用未定义变量直接报错这能避免变量拼错导致问题set -o pipefail表示管道中任何一个环节失败整个管道就算失败。这几项叠加在一起是我写任何脚本的第一行标配。调试脚本时用set -x会逐行打印执行过程把每个命令的实际参数显示出来排查变量展开问题非常好用。写完一个脚本在确认输出没问题之前先跑一遍bash -x script.sh看执行流程比在代码里加一堆echo省事。实操心得写脚本最怕的是“看起来能跑但结果不对”。有一次我写了一个部署脚本用变量拼路径结果变量没赋值导致路径变成/myfile差点把根目录下的同名文件覆盖了。有了set -u这种变量未定义的隐患会在第一时间炸出来而不是悄悄执行出错误结果。我强烈建议所有脚本都加上这一行。6.4 用场景法积累不用词典式背诵最后想多说一句命令的积累靠的是不断遇到场景然后去解决它而不是查一遍命令大全。比如第一次遇到磁盘满你自然会去查df然后查du这个过程长的不是命令记忆而是“什么场景该用什么命令”的对应关系。我自己的做法是准备一个简单的命令备忘单平时遇到一个“哇这个命令组合真好用”的场景就贴进去。实际工作里查自己的备忘单比查搜索引擎快得多而且都是验证过的写法。回到最初的问题Linux 命令多不多多。但真正高频、无废话、能解决问题的就是这一套。把每个命令落实成手头问题的解法比什么都管用。我个人在实操中的习惯是遇到解决过的问题不满足于“能用”而是顺手想想有没有更短的组合方案——比如同一个排查流程能不能少一个管道同一个参数能不能换一个内存占用更小的写法。这种细微的打磨才是用命令从“会”到“熟”的真正分界线。