1. 开篇:shell 命令和文件权限为什么必须放在一起看
刚接触 Linux 的人,几乎都会卡在同一个地方:命令本身背下来了,cd、ls、cp、rm敲得挺顺,可一旦遇到Permission denied、Operation not permitted、Read-only file system这类报错,就完全不知道下一步该敲什么。我在带新人的时候发现一个规律——单纯讲 shell 命令语法,一两个小时就能过一遍;但只要把文件权限模型插进来,整个知识链条才算真正串起来。因为 Linux 里几乎每一条命令的执行结果,最终都要落到“当前用户是谁、对目标对象有什么权限”这两个问题上。
这篇文章想做的事情很明确:把 shell 常用命令和文件权限这两块内容揉在一起讲,从权限位怎么读、数字怎么换算,到chmod、chown、umask、ACL 怎么用,再到权限修复的完整排查流程,最后给一份常见问题速查表。适合谁看?我的判断是三类人:一是刚装完系统、还在熟悉命令行操作的新手;二是接手了别人维护的服务器、经常要处理“文件打不开”“脚本跑不起来”的运维同学;三是准备面试、需要把权限相关考点理清楚的人。文中涉及参数取舍和操作顺序的地方,我都会把背后的原因说清楚,而不是只丢一条命令给你抄。
1.1 一个真实场景:Permission denied 其实有三种完全不同的成因
很多人一看到权限报错,第一反应就是chmod 777。这个习惯很危险。同样一句Permission denied,背后的成因至少有三类,处理方式完全不同。第一类是目标文件或目录本身的权限位不允许当前用户操作,这是最直观的一种,ls -l一看便知。第二类是路径上某一级父目录缺少x权限——注意,这里说的不是目标文件,而是通往目标文件的那一串目录中的某一环,这种情况ls -l看目标文件是正常的,但就是进不去,新手最容易在这里绕圈。
第三类是挂载层面的限制,比如文件系统被挂载成了ro(只读),或者某些安全模块(如 SELinux、AppArmor)对进程做了额外约束,此时权限位看起来完全没问题,chmod也是执行成功的,但写入依然失败。这三种情况在排查手法上完全不同:第一类改权限位,第二类要给父目录补x,第三类要看挂载状态或者安全策略日志。把它们区分开,是权限排查能力的分水岭。
1.2 权限模型为什么长成现在这个样子
Linux 的权限体系继承自早期的 Unix 设计,核心思想非常朴素:谁的东西,谁说了算,别人只能按授权来。每个文件都有一个属主(owner)和一个属组(group),再加上“其他所有人”这一档,形成三段式的权限位。这个设计诞生于多用户分时系统的年代,当时一台机器要同时服务几十个终端用户,必须有一套足够简单、检查开销足够低的授权机制——简单到内核在做权限判定时几乎不产生额外负担,这就是为什么它是九个比特位而不是一张复杂的访问控制列表。
理解了这个历史背景,你就能明白为什么后来会补上 ACL 这个机制:三段式的表达力终究有限,当一个目录要同时给三个不同的人开不同权限时,光靠属主属组就撑不住了。同时也就能理解,为什么 SUID 这类特殊权限位会被设计出来又长期被视为风险点——它本质上是在“简单模型”上打的一个补丁,打破的是“执行时用当前用户身份”这个默认规则。这些都是权限学习里最容易被跳过、但实际上最该知道的部分。
1.3 学习路径的排序建议
我个人的建议是不要按教科书顺序学。先掌握“看”的命令:ls -l、stat、id、whoami,把当前状态摸清楚;再学“改”的命令:chmod、chown、chgrp、umask;最后学“批量处理”和“自动化”的部分:find配合-exec、ACL、脚本里的权限处理。顺序反了会怎样?我见过太多人先学chmod -R 777,结果把整个项目的权限结构彻底打乱,后面想恢复都不知道原始值是什么。先把“读”练熟,再动手改,这是成本最低的路径。
2. 权限位拆解:rwx、属主属组、数字换算到底怎么算
权限这块内容,最忌讳的就是死记硬背。644、755、777这几个数字大家都背得下来,但换个组合比如2755、1777、4755就懵了。真正要吃透,得回到最原始的九位二进制表示上去看,理解了位运算,所有组合都是推导出来的,不需要背。
2.1 从 ls -l 的输出逐字段读起
先看一条典型的输出:
$ ls -l deploy.sh -rwxr-xr-- 1 alice devops 2048 Mar 12 09:14 deploy.sh这一行信息密度很高,从左往右拆:第一个字符-表示这是普通文件,如果是d就是目录,l是符号链接,c是字符设备,b是块设备,s是套接字,p是命名管道。接着九位字符分成三组rwx、r-x、r--,分别对应属主、属组、其他用户的权限。再往后那个数字1是硬链接计数,然后是属主alice、属组devops、字节大小、修改时间、文件名。
这里有个细节很多人忽略:如果权限位末尾出现一个.或者+,比如-rw-r--r--+,那个加号表示这个文件带有额外的 ACL 规则。只改chmod可能改不动实际生效的权限,必须用getfacl看一眼完整的规则集。这个符号我在实际运维中救过好几次场——明明chmod执行成功了,用户就是访问不了,原因就在这个加号上。
2.2 三种身份与数字权限的换算过程
九位权限可以压缩成三位八进制数,换算规则是:r=4,w=2,x=1,每一组内部把有权限的位相加。举几个例子走一遍:
| 权限字符 | 计算过程 | 数字 |
|---|---|---|
| rwx | 4+2+1 | 7 |
| rw- | 4+2+0 | 6 |
| r-x | 4+0+1 | 5 |
| r-- | 4+0+0 | 4 |
| -wx | 0+2+1 | 3 |
| -w- | 0+2+0 | 2 |
| --x | 0+0+1 | 1 |
| --- | 0+0+0 | 0 |
所以rwxr-xr--就是754,rw-r--r--是644,rwxrwxrwx是777。反过来给一个750,你也要能立刻还原成rwxr-x---。
常见的组合对应的使用场景是这样的:644用于普通文本文件、配置文件、网页静态资源,属主可读写、其他人只读;600用于私钥、密码文件、.ssh/id_rsa这类绝对不能让别人读到的东西;755用于目录和可执行脚本;700用于只有自己能进的私有目录,比如.ssh目录本身。记住场景比记住数字有用得多,因为你在设置权限时脑子里应该想的是“谁需要干什么”,而不是“这里该填几”。
2.3 目录的 rwx 和文件完全不是一回事
这是权限学习里最大的认知陷阱。文件上的x表示“可以执行”,目录上的x表示“可以进入(cd)并访问其中的条目”,两者完全不是一个概念。更具体地说:
目录的r权限允许你列出目录内的文件名(也就是ls能出结果);目录的w权限允许你在目录里创建、删除、重命名文件;目录的x权限允许你穿过这个目录去访问里面的具体文件。三者是正交的,可以任意组合。
这里有个非常经典的考点:删除一个文件,取决于父目录的w权限,而不是文件本身的权限。我做过实验,把一个文件设成000,只要父目录对当前用户可写,rm依然能把它删掉。反过来,父目录没有w权限,文件就算设成777也删不掉。原因在于“删除”这个动作修改的是目录项,不是文件内容,所以内核检查的是目录的权限。理解了这一点,很多“为什么我删不掉别人的文件”的疑惑就解开了。
还有一个常见现象值得单独说:一个目录权限是r--但没有x,你ls能看到文件名,但stat任何具体文件都会报Permission denied,尝试cd进去也会失败。这种状态通常出现在有人误用chmod -R 644把目录一起改了的时候——因为644里没有x,目录就变成了“看得见摸不着”。
2.4 特殊权限位:SUID、SGID、Sticky 的实际用途与风险
在三位数字前面还能再加一位,构成四位八进制,这就是特殊权限位:SUID=4,SGID=2,Sticky=1。它们解决的问题各不相同。
SUID(设置后表现为属主执行位变成s)的作用是:程序执行时临时获得文件属主的身份,而