很多人第一次在 Linux 上遇到权限问题,都是从一行Permission denied开始的。我也一样,当年刚接手一台服务器,部署网站时 nginx 反复报错,文件明明就在/var/www/html下面躺着,盯着看了半天也不知道哪里不对,折腾了一下午才搞明白,问题出在我根本没看懂ls -l输出里那一长串字符的含义。从 Windows 转过来的朋友更容易在这个地方懵圈:Windows 遇到权限问题是弹窗,问你要不要给管理员权限;Linux 不是,它直接拒绝,留给你一行冷冰冰的报错。而且印象里“权限不够就提升权限”的直觉,在 Linux 里往往会把事情搞得更糟。
今天这篇文章,我打算把 Linux 文件的权限和拥有者这一套从底层逻辑讲到实操命令,重点放在chmod、chown、chgrp,以及那些一上手就会踩的坑。无论你是刚入门的新手,还是被线上问题缠住的运维和开发,应该都能从里面找到点有用的东西。
1. 从一次Permission denied说起:Linux权限体系的三张门卡
在动手改权限之前,先把模型搞清楚。Linux 对每个文件和目录都记录着三类人的权限:属主(owner)、属组(group)、其他人(others)。你可以把这三类人想成三张门卡——一张卡是文件主人的,一张卡是文件所属组的,一张卡是路过所有人的。至于每个文件允许这三类人做什么,由 9 个字符来记录。
看一个最简单的例子。随便在某个目录下执行ls -l,你会看到类似这样的输出:
-rw-r--r-- 1 root root 1234 Apr 12 10:00 myfile.txt drwxr-xr-x 2 root root 4096 Apr 12 10:00 mydir第一列的第一个字符表示文件类型:-是普通文件,d是目录,l是软链接,b和c是块设备和字符设备。后面 9 个字符分成三组,每组三个,分别对应属主、属组、其他人,每组里依次是r(读)、w(写)、x(执行)。比如-rw-r--r--的意思是:属主可读可写,属组可读,其他人可读。
这里有个细节容易被忽略:权限位缺失的位置必须用-占位,不要想当然地跳过。很多人看-rwxr--r--时会数错位置,以为属组也有执行权限,其实就是没盯住那 9 个字符的排列顺序。在真正修改权限之前,把ls -l的第一列逐位读出来,是必须的基本功。
接下来是判断顺序的问题。当一个进程要访问某个文件时,内核不是“从上到下找符合的权限”,而是先看进程的有效用户 ID(euid)是否等于文件的属主:是,就用属主那三位权限;不是,再检查进程的属组是否落在文件属组里:在,就用属组那三位权限;都不是,才轮到“其他人”那三位。很多新人会犯一个典型错误:以为自己是 root 用户组的成员,就应该能读某个文件,结果文件属于另一个组,不匹配,最后落到 others 位,权限不够就报错。理解了这条判断顺序,你才会明白改权限时到底应该动u位、g位还是o位。
再补充一个反直觉的事实:root 几乎不受文件权限位的约束。它能读能写任意文件,不是因为文件给了它权限,而是内核直接绕过了这层检查。所以你会看到文件权限是000,普通用户进不去,root 照样能改。这不是权限失效,而是 root 天生就是“特权门卡”。正因如此,日常操作尽量少用管理员身份去跑,出了问题排查难度会成倍增加。想确认当前身份,随时用id命令看 uid、gid 和附加组。
提示:排查权限问题前,第一件事永远是用
id确认自己是谁,而不是急着看文件。身份搞错了,后面全白费。
2. chmod实战:数字法与符号法的选型逻辑
权限模型看懂了,chmod才有意义。这个命令的作用是修改文件的权限位,也就是那 9 个字符。它有数字法和符号法两套写法,我几个场景混用了很久,才慢慢摸清各自的适用边界。
2.1 数字法:4、2、1拼出来的权限值
数字法的核心是把三种权限翻译成数值再相加:r=4、w=2、x=1。为什么偏偏是这三个数?因为三种权限的组合总共有 0 到 7 八种情况,正好对应八进制的一位。也就是说,7 = 4+2+1表示可读可写可执行,5 = 4+1表示可读可执行,6 = 4+2表示可读可写。每组三位数字从左到右分别代表属主、属组、其他人。
所以chmod 754 file的含义是:属主拥有全部权限(7),属组可读可执行(5),其他人只读(4)。具体数值和对应关系可以看这张表:
| 数字 | 权限位 | 含义 | 常见用途 |
|---|---|---|---|
| 7 | rwx | 读 + 写 + 执行 | 脚本、目录 |
| 6 | rw- | 读 + 写 | 普通文档 |
| 5 | r-x | 读 + 执行 | 程序、目录 |
| 4 | r-- | 只读 | 只读文档 |
| 0 | --- | 无权限 | 敏感文件 |
这里有一条经验:文件不要随便给执行位,除非它真的是脚本或程序;目录则通常至少给5,也就是可读可执行,否则别人连目录都进不去。很多刚入门的朋友给目录设成644,结果发现目录只能看到列表却进不去,本质就是少了x位。
2.2 符号法:精确修改某一位
数字法的缺点是,我只想给文件加一个执行权限,还得先心算当前值是多少。这时符号法更方便,语法是“对象 + 操作符 + 权限”三段式。对象有u(属主)、g(属组)、o(其他人)、a(所有人);操作符有+(添加)、-(移除)、=(设置为);权限就是r、w、x。
chmod u+x script.sh # 给属主加执行权限 chmod g-w file.txt # 去掉属组的写权限 chmod o=r file.txt # 把其他人的权限设为只读 chmod a+rx appdir # 所有三类人都加上读和执行符号法的优势在交互式操作时特别明显:不用算数值,改完立马ls -l验证。而且它的操作是“增量式”的,只影响你指定的那一位,不会像数字法那样要求你一次性写全整个 9 位,误操作的概率低不少。
2.3 脚本用数字、手调用符号
我自己习惯的做法是:写部署脚本、自动化配置时用数字法,因为数值是确定性的,同一个命令在任意环境跑出来结果完全一致,方便记录和排错;交互式排查问题时用符号法,因为不用心算,也减少误改其他位的概率。两种方法最终改的都是同一个权限矩阵,没有孰优孰劣,只有场景适配之分。比如在 Ansible 这类配置管理工具里,一律写数字法,别人看代码时一眼就能知道目标状态是什么。
2.4 -R 递归别乱用
chmod -R非常方便,也最容易出事。假设你执行chmod -R 777 /data/,整个目录树下的所有文件、目录、子目录全变成 777,任何人能读能写能执行,这在生产环境几乎是灾难。更稳的做法是分开处理:先找出所有目录,统一给755;再找出所有普通文件,统一给644。命令可以这样写:
find /data -type d -exec chmod 755 {} \; find /data -type f -exec chmod 644 {} \;如果时间紧,非要一条chmod -R,那也要在跑之前确认:目录下没有私钥、没有配置文件、没有临时目录。我见过太多线上事故是“顺手一个-R 777”导致的,真的不要抱侥幸心理。
3. chown与chgrp:改拥有者之前先想清楚两件事
chmod改的是“权限矩阵”,chown改的是“这张文件归谁”。权限设置得再漂亮,如果文件的属主不是运行服务的那个用户,依然会报Permission denied。所以chown在运维里的出场率一点都不比chmod低。
3.1 基本语法和常见组合
chown alice file.txt # 把文件属主改成 alice chown alice:devops file.txt # 同时修改属主和属组 chown :devops file.txt # 只改属组,属主不动 chgrp devops file.txt # 等价于 chown :devops注意alice:devops中间是冒号,不是点号。老版本 Linux 可能兼容点号写法,但新环境建议统一用冒号,避免在脚本里出现解析问题。如果只想改目录本身,不想动里面的内容,就别加-R;如果确实要递归改整个目录树,再考虑用-R。
3.2 什么时候必须改拥有人
实际场景里,chown用得最多的是这么几类:
- 从压缩包解压出来的文件,属主往往是你本机用户,但服务器上运行服务的用户是
www或nginx,就得执行chown -R www:www /var/www/html,把整个站点目录交给服务账户。 - 挂载外部磁盘后,文件属主显示为某个 uid 数字,比如 1000 或 1001,而服务器上的服务账户是另一个 uid,需要按实际 uid 重新归属。
- 团队协作时,把项目目录的属组改成共享组,大家在同一个组里读写,就不用互相
chmod 777了。
一个常见问题是:文件属主改成谁?我一般遵循“谁运行谁拥有”的原则。文件由 nginx 进程读写,就归 nginx 用户;由某个业务账户读写,就归那个账户。别只看自己当前登录的用户名,要把“运行时身份”和“管理时身份”分开。
3.3 递归修改的边界意识
chown -R和chmod -R一样,递归操作一定要先确认边界。我给自己定的规矩是:执行前先pwd确认当前路径,然后写完整路径,再想一下“这个目录下的所有内容我都确定要改吗”。尤其是,绝对不要让chown -R碰到/usr、/etc这类系统目录。系统文件的所有者一旦被改乱,服务起不来、用户登录不上,排查起来极其痛苦。真不小心改了系统目录,也别慌,优先检查哪些目录的属主不对,用发行版自带的包管理器重新校验并恢复文件属性,比如 rpm 系的rpm -Va,deb 系的dpkg --verify,能少走很多弯路。
4. 目录权限是另一套玩法:为什么755的目录能进、644的进不去
文件权限和目录权限看着是同一套rwx,语义却完全不同。很多人把文件的权限思维直接套到目录上,结果就是各种各样“明明给了权限却还是不行”的怪问题。
4.1 目录上的读写执行分别是啥意思
| 权限位 | 文件上的含义 | 目录上的含义 |
|---|---|---|
| r | 读取文件内容 | 列出目录内容(能看到文件名) |
| w | 修改文件内容 | 在目录中创建、删除、重命名条目 |
| x | 执行文件 | 进入目录、访问目录内的文件 |
关键在x。目录没有x权限,即使你拥有r权限,也只能看到文件列表,无法cd进去,也无法访问目录内任何文件。所以目录通常至少是5(r-x),能看能进,但没有写权限,大家只读不写。常见的目录权限是755:属主可写,其他人都只能读和执行。如果你想建立一个只允许属主进入的目录,那就是700。
4.2 删文件不看文件权限,看目录权限
这是一个非常容易被忽略的盲区:删除一个文件,系统检查的是你对该文件所在目录是否有写权限,而不是文件本身的权限。也就是说,在一个大家都可写的目录里,只要你对目录有w权限,就能删掉别人的文件,哪怕那个文件自己是000。
反过来也成立:一个文件是777,但你没有它所在目录的写权限,你删不掉它,最多只能修改文件内容(如果你对它本人有写权限的话)。这个理解在实际运维里极其重要。比如用户反映“我对某文件有写权限,但保存失败”,先去检查它所在目录权限,问题经常在目录那一层。
因为这个特性,公共可写目录必须配合粘滞位使用。/tmp就是一个典型例子,它的权限是drwxrwxrwt,最后一位t就是粘滞位,后面我会详细讲。没有粘滞位的公共可写目录,等于大家可以互相删文件,乱套是迟早的事。
5. SUID、SGID、Sticky Bit:容易被忽略的三个特殊权限位
前面的9位权限是基础,但 Linux 文件权限里还有三个特殊权限位,平时不起眼,关键时刻很有用,也容易埋雷。它们分别是 SUID、SGID 和 Sticky Bit。
5.1 SUID:为什么普通用户可以改自己的密码
看passwd命令的权限:
-rwsr-xr-x 1 root root 68208 ...注意属主那组权限多了一个s,这就是 SUID(Set User ID)。它的含义是:当普通用户执行这个程序时,进程的有效用户 ID 临时变成 root,而不是执行者本人。这样一来,普通用户才能去修改/etc/shadow里自己的密码条目,因为那个文件只有 root 能写。
SUID 不是玩具。如果某个程序被设置了 SUID,又存在可利用的漏洞,攻击者等于拿到了一把 root 权限的钥匙。生产环境里,定期用find / -xdev -type f -perm -4000 -ls检查系统中有哪些 SUID 文件,是必要的安全巡检项。设置 SUID 用chmod u+s file或数字法chmod 4755 file。
5.2 SGID:让同组的人生成的文件自动属于组
SGID 也有类似机制,文件上设置 SGID 会让进程临时获得文件属组的权限。但更常用的场景是在目录上设置 SGID:在设置了 SGID 的目录下新建的文件,默认属组会继承目录的属组,而不是创建者的默认组。这对团队协作非常友好,大家往同一个共享目录里放文件,自动归到同一个组,不用每次都手动chgrp。
设置方式是chmod g+s dir,用ls -l看显示为drwxrwsr-x。注意那个s的位置在属组权限的x位上,大写S表示没有执行权限时的 SGID,小写s表示有执行权限时的 SGID,别混淆了。
5.3 Sticky Bit:/tmp 目录为什么不乱套
再看/tmp的权限:
drwxrwxrwt 20 root root 4096 ...最后一位t就是粘滞位(Sticky Bit)。它的作用是:在这个目录下,除了 root,只有文件的属主(或目录属主)能删除或重命名文件,其他人即使有目录写权限也动不了你的文件。换句话说,/tmp是公共可写目录,但因为有粘滞位,A 用户不能删 B 用户的临时文件。
设置方式是chmod o+t dir,显示为drwxrwxrwt。公共可写目录强烈建议加上粘滞位,不加就是裸奔。
5.4 特殊权限位的数值写法
三个特殊权限位也可以用数字表示:在传统三位八进制前面多加一位,4表示 SUID,2表示 SGID,1表示 Sticky。例如chmod 4755、chmod 2770、chmod 1777。不过如果是在交互环境里操作,我更推荐用符号法,因为数字法写错一个 bit 很难一眼看出来,读起来也不直观。无论用哪种,设置完一定要ls -l确认结果。
6. 权限报错排查链路:从报错信息到定位根因的完整思路
线上遇到Permission denied时,最怕的就是瞎试。一会儿chmod 777,一会儿chown自己,试完了问题还在,时间也浪费了。我一般按下面的顺序走,定位快很多。
6.1 排查的九个步骤
- 先确认当前身份:
id,看 uid、gid 和附加组。 - 看目标对象的真实权限:
ls -l或stat目标文件。 - 逐层检查路径上每一级目录的执行权限。
- 检查目录是否有写权限(针对删除、新建操作报错)。
- 用
namei -l /完整/路径一次看清每一级路径的权限和属主。 - 看挂载选项:
mount或findmnt,关注ro、noexec、nosuid、nodev。 - 考虑 ACL:
getfacl看是否有setfacl设置的额外规则。 - 看 SELinux:
getenforce,如果是 Enforcing,还要考虑安全上下文问题。 - 看日志:
journalctl、dmesg、应用自身日志,常有明确提示。
很多朋友步骤 1 和 2 都不做,直接跳到第 4 步甚至第 7 步,这样很容易南辕北辙。我也是踩过几次坑之后才养成“先查身份、再看权限”的习惯。
6.2 一次典型排障案例
某次同事反馈,Nginx 站点下有个上传目录一直报 permission denied。我先id看了一眼 Nginx 的 worker 进程用户是nginx,再ls -l看上传目录,权限是755,属主是root。问题线索已经很清晰:755目录属主 root,worker 进程用 nginx 身份,不在属主位,也不在属组位,属于“其他人”,“其他人”只有r-x,没有w,所以上传写文件失败。定位后执行chown nginx:nginx upload_dir,问题解决。
整个过程花了不到一分钟,因为每一步都有明确的验证手段。我见过不少同事在这个场景里直接chmod 777,也能解决问题,但代价是所有人都能往里面塞文件,安全隐患极大。能精准定位,就不要用“权限全开”这种粗暴手段。
6.3 几个让人多花时间的误区
第一,以为 root 报错都是“权限太大”才报错。实际上 root 报错多半是文件系统只读或 SELinux 拦截,跟权限位无关,这时候查mount和getenforce才是正路。第二,忽略了粘滞位。某些公共目录明明该删的文件删不掉,先怀疑粘滞位,而不是怀疑权限不够。第三,双眼只盯着文件本身,忘了检查父目录。路径上任何一级目录缺少x,都会卡死后面的所有访问。这些误区我全都踩过,每次都是回到上面的排查链路才把问题找出来。
7. 权限规划指南:别等出问题再去救火
权限和拥有者管理的本质,是“谁应该碰这个文件”的决策。与其每次等报错再去救,不如一开始就把规则想清楚。
7.1 umask 决定新文件的默认权限
新建文件和目录的默认权限由umask决定。默认设置一般是022,对应“文件644、目录755”,意思是新建的东西默认不给组和其他人写权限。如果你希望新文件默认对组可写,可以把umask改成002,那新建的文件就是664、目录是775。团队协作时,统一umask是有价值的规范,否则不同成员各自的umask不一致,就会出现“他创建的文件我只能读”这种琐碎问题。
7.2 最小权限原则的常用搭配
| 场景 | 推荐权限 | 原因 |
|---|---|---|
| 普通配置文件 | 644 | 属主可写,其他只读 |
| 脚本文件 | 755 | 属主可写可执行,其他可读可执行 |
| 私钥文件 | 600 或 400 | 绝不能给组和其他任何权限 |
| 共享目录 | 775 或 2770 | 组内协作,杜绝其他人写入 |
| 公共读目录 | 755 | 属主可写,其他人只读 |
私钥这类敏感文件,我通常直接600,组内也不给读,宁可授权麻烦一点,也别留后门。共享目录如果希望新建文件自动继承组,就在775基础上加 SGID,也就是2770,效果会更好。
7.3 周期性巡检习惯
最后提一个小习惯:隔一段时间跑一条find命令,把可疑的高危权限文件列出来,看看有没有新增的777文件或 SUID 文件。
find / -xdev -type f -perm -0002 -ls 2>/dev/null # 全用户可写的普通文件 find / -xdev -type f -perm -4000 -ls 2>/dev/null # SUID 文件这种巡检不需要每天做,但可以在每周运维清单里加一条。权限问题最怕的不是复杂度,而是临时改完忘记还原。一个777的文件在那里挂一个月,没人知道它是什么时候出现的,但风险一直在。
最后分享一点个人体会。我在实际使用中的习惯是:每个项目建一个专用用户和一个专用组,文件默认归属这个组,不同角色按实际需要给不同权限。看起来是前期多花了几分钟,但后面省下的是无数个深夜排查。建议你拿到一台新机器,先在虚拟机里把chmod、chown、namei、stat挨个跑一遍,跑熟了再上真实环境,遇到报错心态会稳很多。