
1. 为什么说Linux指令是绕不过去的基本功1.1 指令不是背出来的是“用”出来的我刚开始接触Linux的时候跟大多数人的想法一样以为这就是一堆需要硬背的命令行单词。ls、cd、cat、grep、find背来背去结果过两天全忘了。后来跟着一个老运维做线上环境支持才慢慢明白一件事——Linux指令的真正价值不在于“记住”而在于“理解”。这台系统里所有你能看到的东西文件也好、进程也好、网络连接也好本质上都是系统资源的一层抽象。而指令就是你跟这些资源打交道的入口。你敲一条ls看起来是列目录背后是shell帮你调用了系统的readdir接口你敲一条kill表面上是一个命令本质上是向进程发送一个信号。认识到这一层再看任何指令就不会觉得它是一堆孤立的字符了而是一个个有逻辑关联的动作。所以这篇文章的核心目标不是给你列一份“常用命令大全”然后让你背而是带你把这些命令背后的逻辑捋清楚它们解决什么问题、为什么这样设计、在真实场景里怎么组合使用。适合刚入门想系统认识Linux的人也适合已经会用一二十条命令但总觉得“不够透”的开发者。1.2 指令与系统底层原理的关系很多人觉得“底层原理”是内核工程师才需要关心的事情普通开发不用碰。这个看法会限制你的排查能力。举一个简单的例子你在生产环境看到磁盘空间满了第一个反应是df -h发现挂载点使用率100%于是开始找大文件。但如果你只停留在“用命令”的层面能找到问题如果你知道Linux的ext4文件系统里文件占用的块不是连续存储的删除一个文件只是释放inode和对应的数据块指针并不会立刻把磁盘上的数据抹掉那你就会明白为什么有时候删了大文件空间却不释放——因为那个文件还被某个进程占用着。这时候你用lsof | grep deleted一次就能定位问题。这就是指令和底层原理的关系指令是“招式”原理是“内力”。招式背得再多碰到底层变化就抓瞎内力够了即使遇到没见过的指令你也能凭借对系统行为的理解猜出大概方向再配合man文档把它吃透。这篇文章会尽量帮你把“招式”和“内力”串起来讲。2. 高频指令的底层逻辑与常见误区2.1 文件和目录ls、cd、stat背后的对象模型Linux里头最核心的概念就是“一切皆文件”。普通文件是文件目录是文件设备是文件socket也是文件。你理解了这一层再去看很多命令就会觉得顺理成章。先拿ls来说。很多人只知道ls -l看权限却不知道这条命令背后读的是inode里的元数据。一个文件在磁盘上真正存放内容的数据块和描述它的元信息是分开的。元信息存储在inode里包括文件大小、权限、属主、时间戳、数据块位置文件名则放在目录项里目录项通过inode号指向具体的inode。所以当你执行mv命令重命名一个文件其实只是修改了目录项里的名字映射inode和数据都没动。这就是为什么mv同一分区内的文件速度极快而跨分区就慢——跨分区时要先复制数据到新分区再写新的目录项最后删除旧链接。用stat命令可以清楚看到这个结构stat /etc/hosts输出里有File、Size、Blocks、Inode、Links等字段。其中Links是硬链接数量。这里要特别提一下硬链接和软链接的区别硬链接是多个目录项指向同一个inode删掉其中一个名字只要还有别的链接数据就还在软链接symlink是生成一个新的文件内容里存的是“目标路径”如果目标被删了软链接就变成了断链。我见过不少同事在部署应用时用软链接切版本ln -sfn new_version current这个操作很常见也很实用。但是有一个坑如果你在软链接目录里执行pwd你会发现它指向的是物理路径还是链接路径取决于你的shell是否解析了PWD环境变量。cd进软链接目录后执行pwd默认显示物理路径但如果你设置了环境变量PWD不走解析就可能出现“当前目录”和“实际路径”不一致的错觉。处理这类问题的时候用pwd -P强制显示物理路径用pwd -L显示逻辑路径能省很多无谓的纠结。2.2 进程管理ps、top、kill怎么配合才有效进程管理是Linux运维里最常踩坑的领域。ps aux是最常用的命令它的输出格式不少新手看着发懵。USER、PID、%CPU、%MEM、VSZ、RSS、TTY、STAT、START、TIME、COMMAND每一列都有信息来源。进程平均占用要开着耗CPU要开着内存是否吃紧也要看RSS但这里有个容易忽视的细节RSS不包含被换出到swap的页面也不包含共享内存中属于其他进程的部分。所以说“进程占用内存”要结合free来看不能只看RSS。看进程状态STATE的时候你会见到S、R、D、Z、T等字母。S是可中断睡眠R是运行中D是不可中断睡眠——这个状态在排查IO瓶颈的时候特别关键。进程卡在D状态多半是在等磁盘IO你kill都kill不掉因为它不响应信号。这个时候你要去查是不是存储设备出了问题而不是反复敲kill -9。Z是僵尸进程意思是子进程已经退出但父进程没有调用wait回收它的状态信息。一个两个僵尸无所谓系统清理不了是因为父进程还活着但如果堆积成百上千个就要考虑是不是父进程的代码写得有问题把wait漏掉了。再看kill。kill -9是很多人的救命稻草但我劝你不到万不得已不要一上来就-9。kill默认发的是SIGTERM15给进程一个优雅退出的机会让它自己清理资源、保存状态、关闭连接。SIGKILL9是强制终结内核直接回收进程不给它任何善后机会。对于一些关键服务比如数据库直接kill -9可能导致数据文件损坏或需要长时间恢复。正确的做法是先用kill加TERM信号等几秒不行再逐步升级。热词里有个“让后台运行指令不因界面退出而退出”这个真的太实用了。最朴素的做法是nohup command nohup的作用是让进程忽略SIGHUP信号这样你关掉终端时shell给子进程发的挂断信号就被忽略了。但nohup有个局限它只是忽略挂断信号如果进程本身因为其他原因退出它也管不了。更干净的做法是用setsid让进程彻底脱离当前会话成为新的会话首领。最稳妥的当然还是systemd托管写一个unit文件由init进程拉起开机自启、崩溃自动重启只是初期配置成本稍高。2.3 权限模型chmod、chown和umask的安全边界权限问题几乎是每个Linux新手都会绕进去的迷宫。你敲一条命令系统告诉你Permission denied第一反应就是sudo这其实是一个坏习惯。Linux的权限模型其实很清晰每个文件有一个属主owner、一个属组group然后按照“属主权限”“属组权限”“其他人权限”三组来判断。每组又是读r4、写w2、执行x1三个位用数字表示就是422相加所以755就是属主可读写执行属组和其他人只读执行。这里要提醒目录的执行权限不是“运行”而是“能否进入目录并访问其中文件”。就算某个目录对其他用户没有任何权限只要它有执行权限用户依然可以cd进去前提是你知道目录里的文件名。chown用来改属主属组chmod用来改权限位。这两个命令配合起来能解决大部分授权问题。但比命令本身更重要的是理解umask。umask决定了你新建文件的默认权限。它是一个“反掩码”表示要从默认权限里去掉哪些位。比如umask是022二进制就是--- -w- -w-也就是说对属组和其他人去掉写权限于是新建目录的权限是755新建文件是644。搞清楚这个逻辑你才能解释为什么明明设置了umask 000新建文件却还是664甚至666——这不光是umask一个因素和文件系统的挂载参数也有关系。顺带说一下SUID和SGID。SUID会让可执行文件以属主身份运行而不是以当前用户身份运行。最典型的例子是passwd命令它需要写/etc/shadow而普通用户没权限但因为有SUID位执行时临时获得root能力。这些特殊权限位是排查“为什么这个命令能访问我没权限看的东西”时的重要线索用ls -l一眼就能看出来属主执行位是s而不是x。2.4 网络排查从ip、ss到抓包定位服务器出问题十次里有八次跟网络有关。而排查网络问题最忌讳的就是瞎猜。先说ip命令。ip addr看接口和IPip route看路由表ip link看链路状态。以前老系统里用的ifconfig现在很多发行版默认不装了建议直接学ip系列。查不通的时候第一步不是ping外部而是按照“本机回环—本机IP—网关—外部”的顺序逐层测。再说ss。ss是socket statistics的缩写取代了老的netstat。你把ss -tunlp一敲就能看到当前机器上监听的所有TCP和UDP端口还能看到进程PID。服务器端口被占了第一反应就该是这条命令。实际排查一个“连不上服务”的问题一个实用路径是这样的ss -tulnp | grep 端口号 # 确认监听是否正常 curl -v http://127.0.0.1:端口 # 本机测通排除外部网络因素 ping -c3 网关IP # 看二层三层通不通 traceroute -n 目标IP # 看路径在哪一跳断掉如果本机curl通了那就是防火墙或外部网络的问题如果本机都不通问题在服务本身或监听地址配置上。这个思路一旦建立排查速度快很多。抓包工具tcpdump也是网络排查的终极手段。命令本身不复杂tcpdump -i eth0 port 80 -w /tmp/http.pcap但难点在于读包、分析包需要了解的协议细节较多。不要求人人精通但至少要知道当所有表象信息都查不出问题时抓包能帮你看到最底层的事实。3. 实操一套可直接上手的日常命令组合3.1 从登录到定位瓶颈的完整链路有了前面的基础真正的执行力要求“到机器上知道先干什么”。我通常把这个过程固定成一套动作不管云服务器还是物理机基本通用。登上去第一步不看别的先看系统整体负载。用uptime看平均负载load average后面的三个数字分别代表1分钟、5分钟、15分钟的系统平均负载。如果1分钟比15分钟高很多说明负载正在上升反过来说明高峰已经过去了。这里要分清负载高并不一定等于CPU忙它还包含不可中断睡眠的进程数。所以负载高的时候继续往下定位是CPU问题还是IO问题。第二步看CPU和内存。top命令进去后按大写P按CPU排序按大写M按内存排序。但top有个不方便的地方——它是交互式的不适合脚本化采集。这时候用vmstat 1 5一次性采样5秒看r列运行队列、b列阻塞进程、us、sy、id、wa。如果wa很高说明CPU在等IO问题不在计算而在存储。再用iostat -x 1 5看磁盘util和await基本就能锁定瓶颈。第三步看日志。系统日志一般都在/var/log下但不同发行版路径不完全一样。systemd时代以后journalctl成了主流。查最近一次启动以来的错误信息用journalctl -p err -b就能看从本次启动以来的error级别日志。查某个服务的日志journalctl -u nginx.service --since 10分钟前。这些操作比去日志目录里翻文件来得高效得多。这一套组合拳下来一台机器从“感觉卡”到“定位到具体瓶颈”耗时不会超过五分钟。相比一上来就敲top然后看着数字发呆这种有目的性的排查链路才是真正的工作经验。3.2 让命令在后台稳定运行的正确姿势如果你只是偶尔需要让一条命令在后台跑nohup和setsid完全够用。但如果你在跑的是一个需要长期稳定的服务这几个土办法都撑不住。为什么因为nohup只负责忽略SIGHUP进程本身如果因为内存溢出、或者父进程被莫名其妙杀掉了它是不会自己拉起来的。我实际在服务器上跑数据处理脚本的时候踩过一个很深的坑。那段脚本需要处理十多个大文件整体耗时一小时左右。我用nohup python script.py 把它放到后台记录输出到nohup.out然后关掉终端回家了。第二天回来看脚本跑了一个半小时数据确实处理完了但输出的日志全在nohup.out里根本没有按我预想的分级存文件。排查了半天发现进程确实活着但因为终端关闭时进程的stdout和stderr都被指向同一个日志文件缓冲不刷新导致部分输出丢失。从那以后我总结出一个基本方案长时间跑的脚本一定要写成systemd服务哪怕只是简单的ExecStart一行[Unit] DescriptionMy Long Running Task [Service] ExecStart/usr/bin/python3 /opt/mytask/run.py Restartalways RestartSec5 Userdeploy [Install] WantedBymulti-user.target把文件放到/etc/systemd/system/下命名成my-task.service再systemctl daemon-reload然后systemctl start my-task。这样即使进程因异常退出systemd会在5秒后自动拉起开机也会自动启动。输出日志交给journald统一管理用journalctl -u my-task -f实时查看。对于正经跑业务的服务这是唯一推荐的方式。如果只是临时任务又不想写unit文件另一个折中的办法是用tmux。tmux里开的窗口和终端是解耦的关掉SSH不会影响已经建立的会话。先tmux new -s task在窗口里跑命令然后Ctrlb按d退出会话下次再tmux attach -s task回来继续看非常灵活。对于“人需要在现场观察但不想一直挂着终端”的场景这是最顺手的方案。3.3 用管道和重定向把命令串起来单条指令只是积木真正解决问题靠的是组合。Linux指令的粘合剂就是管道符|和重定向。管道的原理是shell把前一个命令的stdout接到后一个命令的stdin中间由内核创建一个pipe缓冲区。这条链路上的每个进程并发执行上游产出的数据流进管道下游的进程同步消费而不是等上游全部跑完才开始。一个常见的组合是查询某个进程并清理它ps aux | grep nginx | grep -v grep这条命令会把所有含“nginx”的行过滤出来但你经常会在结果里看到一条grep自身的记录因为grep nginx这个进程自己也匹配到了“nginx”。所以第二个grep -v grep把它排除掉。这个技巧看起来简单但不懂的人第一次看到这个命令总会问“为什么有一条grep在结果里”。另一种组合是批量处理文件。查找所有大于100M的日志文件并删除find /var/log -type f -size 100M -name *.log -exec rm -f {} \;这里-exec后面是一个小动作模板{}代表find找到的每一个文件结尾的;表示命令结束。用find的时候要特别小心用-exec比用管道再接xargs更安全因为管道方式对包含空格的文件名会有问题除非你让find输出以null结尾再配合xargs -0。再看一个经典的日志压缩任务把三天前的所有.log文件压缩成.gz格式再删除原文件。有人会写for循环其实一行命令就能处理find /var/log -name *.log -mtime 3 -exec gzip {} \;gzip命令会直接把.log文件替换成.log.gz文件。这里面的-mtime 3表示修改时间超过3天单位是24小时。如果你想删除的是“三天前的今天”产生的文件得用-mtime 2配合具体时间范围这个细节最容易出错。4. 常见问题与排查技巧实录4.1 命令挂起、输出乱码、权限拒绝的排查思路“命令敲下去没反应”是每个人都会遇到的情况。表面上看卡死了但原因可能完全不一样。最常见的是前面提到的D状态进程也就是不可中断睡眠。如果你发现某个命令一直不返回先另开一个终端ps aux看它现在处在什么状态。如果是D说明它在等IO磁盘或者网络存储可能出了问题如果是T说明被暂停了一般是按了CtrlZ可以用fg恢复如果状态是R但CPU占用极低那可能是锁竞争比如文件锁或者数据库行锁。终端乱码也是一个高频问题。很多人以为是系统坏了其实是语言环境不一致。服务器输出UTF-8编码的日志你的Windows终端用GBK解码自然就乱码。处理办法不是改服务器而是在SSH客户端设置UTF-8编码或者执行export LANGen_US.UTF-8强制指定语言环境。服务器上的日志文件用file命令看编码再决定用什么方式打开比瞎猜靠谱得多。权限拒绝的排查顺序我总结成一个固定的套路先看当前用户身份whoami、id再看目标的属主属组和权限位ls -l然后看父目录权限是否允许你进入最后考虑ACLgetfacl和SELinuxgetenforce。90%的权限问题出在前三步剩下10%是ACL和SELinux的锅。很多系统默认开启SELinux文件权限看起来没问题还是告诉你无权限这时候检查/var/log/audit/audit.log或者临时用setenforce 0验证一下思路瞬间清晰。4.2 误操作修复经验rm、重定向覆盖的惨痛教训聊到指令就绕不开误操作。我自己的黑历史是被一条rm -rf触发过从那以后的规则特别简单非必要不直接rm一律先mv到/tmp或专门的trash目录。即使真要删也是先用ls确认路径再手动敲完整路径绝不用通配符拼模糊路径。alias rmrm -i这条alias可以加进~/.bashrc至少能让你每次删除前确认一下。不过它也有一个副作用——在脚本里如果你用了非交互式shellalias默认不生效所以它防的是手工操作防不了脚本风险。另外一条更进阶的保护用find删除文件的时候先不带任何动作只打印匹配项确认无误再上-execfind /data -type f -name *.tmp # 先看 find /data -type f -name *.tmp -delete # 确认后执行重定向覆盖也同样危险。command file这种形式会直接创建或截断目标文件如果你本来想附加内容却写成了覆盖之前的内容就没了。我的习惯是凡是需要保留历史的输出一律用只有在确认“这个文件没用”的情况下才用。把命令写进脚本的时候这个习惯能挡住很多意外。4.3 快速查询指令用法的最佳实践既然题目叫“指令认识”那就一定绕不开“到了关键时刻想不起命令怎么办”这件事。我的建议是分三个层次。第一层是内置帮助。man page是Linux系统自带的最完整手册例如man ls、man bash。但man的输出实在太多一屏装不下很多人看一眼就关了。相比之下命令自带的--help更精炼比如grep --help输出的都是选项概要。另一个实用命令是whatis一条命令用一句话说明它是干什么的whatis tar第二层是history。你在历史里敲过的每一条命令都能用history显示。翻历史是有捷径的CtrlR进入反向搜索输入关键字自动补全比如输入“nginx”它会帮你匹配最近敲过的含nginx的那条命令多敲几次CtrlR继续往前翻。效率比手打完整命令高十倍。第三层是工具类辅助。现在很多发行版可以直接安装tldr它把man手册的核心用例提取成简洁示例对新手极其友好。比如tldr tar会直接告诉你“把目录打包成tar.gz”“从tar.gz解压”“只查看不解压”分别怎么写。在命令行时代学会查文档比记住一切更重要知识是拿来检索的不完全是拿来背的。5. 关于Linux指令学习路径的一些个人体会文章写到这儿核心内容基本聊完了。最后想聊几句不太像教程、但我觉得更重要的东西。Linux指令这个领域信息不稀缺稀缺的是稳定可靠的实践场景。看再多的命令清单都不如自己找一台虚拟机、配一个简单的服务、故意把服务弄坏再亲手修好来得扎实。我见过有的人把一份命令大全背得滚瓜烂熟可真正出了问题连服务器日志放哪都找不着。反过来那些命令记得不太全的人只要思路清晰——先看负载、再看进程、再查日志——照样能解决大部分运行故障。我自己的学习习惯是每遇到一个新的操作场景就把实际用到的命令和思考过程记下来不写命令本身写“为什么要这样敲”。过一段时间回看这些笔记比任何教程都有价值。比如我笔记里有一条是“为什么nohup跑出来的进程有时候还会被系统杀掉”顺着这个问题我学会了看进程的退出码、翻journalctl的日志、理解systemd的资源限制参数。从一个命令冒出的问题几乎可以把你引向整个系统运行机制的深处。真心建议各位不用给自己太大压力今天懂十条命令和懂一百条命令差别没有想象中那么大。真正重要的是那十个命令里的每一个你都清楚它在系统里做了什么、为什么会有这样的输出、出错时应该往哪个方向排查。一条命令认识透了后面就是触类旁通的事。