chmod 命令详解与参数实测(Ubuntu 22.04)

GNU chmod 参数实测:数字与符号模式、裸 +x 与 umask、大写 X 递归差异、--reference、setuid/setgid/sticky 三特殊位(组继承与删除保护)、suid 对脚本无效,Ubuntu 22.04 实测。

Linux命令Linux系统UbuntuUbuntu 22.04命令行用户权限2026/10/15

chmod 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 chmod 的每一个常用参数——数字模式(644/600/755/4755/2775/1777)、符号模式(ugoa 与 +-=、rwxXst)、裸 +x 与 umask 的关系、-R 递归与大写 X 的目录/文件差异、--reference、-c/-v/-f,以及 setuid/setgid/sticky 三个特殊权限位的完整实测(含 setgid 目录组继承、sticky 删除保护、suid 对 shell 脚本无效),全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。

一、环境信息

本篇所有命令与回显均来自同一台 Ubuntu 22.04 服务器的实测,演示数据统一放在 /root/demo/chmod/ 下,另在 /tmp/u4d-demo/ 做普通用户权限实验。读者在任意 Ubuntu 22.04 机器上按相同步骤均可复现。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • chmod 版本:GNU coreutils 8.32(chmod --version 实测)
  • chmod 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
  • 演示目录:/root/demo/chmod/(root 身份操作);/tmp/u4d-demo/(普通用户实验)
  • 测试用户与组:u4d1(uid=1000,主组 u4d1,附加组 u4dgrp1)、u4d2(uid=1001,主组 u4d2,附加组 u4dgrp1/u4dgrp2)、u4dgrp1(gid=1001)、u4dgrp2(gid=1002)
  • 准备命令(已存在的用户/组会报 File exists,忽略即可):
useradd -m u4d1 && useradd -m u4d2
groupadd u4dgrp1 && groupadd u4dgrp2
usermod -aG u4dgrp1,u4dgrp2 u4d2

二、命令概览

2.1 语法与测试数据

chmod 的语法共三种形态(摘自帮助 Usage 行):chmod [OPTION]... MODE[,MODE]... FILE... 符号模式、chmod [OPTION]... OCTAL-MODE FILE... 八进制模式、chmod [OPTION]... --reference=RFILE FILE... 参照文件模式。帮助末尾一句话就是模式语法的完整定义:Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'——符号模式由 who(ugoa)+ 操作符(+-=)+ 权限(rwxXst 或 ugo)组成,八进制模式允许带 +-= 前缀。

准备演示数据——三个普通文件、一个脚本、一棵带子目录的目录树、一个参照文件:

mkdir -p /root/demo/chmod/d1/sub
echo 'chmod demo' > /root/demo/chmod/f1.txt
echo 'chmod demo' > /root/demo/chmod/f2.txt
echo 'chmod demo' > /root/demo/chmod/f3.txt
printf '#!/bin/bash\necho hello chmod\n' > /root/demo/chmod/script.sh
printf '#!/bin/bash\necho run inner\n' > /root/demo/chmod/d1/run.sh
echo inner > /root/demo/chmod/d1/f.txt
echo refperm > /root/demo/chmod/ref.txt

验证数据就绪(全部是 root 的默认 644/755):

$ ls -l /root/demo/chmod
total 24
drwxr-xr-x 3 root root 4096 Oct 15 09:01 d1
-rw-r--r-- 1 root root   11 Oct 15 09:01 f1.txt
-rw-r--r-- 1 root root   11 Oct 15 09:01 f2.txt
-rw-r--r-- 1 root root   11 Oct 15 09:01 f3.txt
-rw-r--r-- 1 root root    8 Oct 15 09:01 ref.txt
-rw-r--r-- 1 root root   29 Oct 15 09:01 script.sh

2.2 --help 全量输出

chmod --help 输出全部参数清单(实测原样保留,可作为参数速查):

Usage: chmod [OPTION]... MODE[,MODE]... FILE...
  or:  chmod [OPTION]... OCTAL-MODE FILE...
  or:  chmod [OPTION]... --reference=RFILE FILE...
Change the mode of each FILE to MODE.
With --reference, change the mode of each FILE to that of RFILE.

  -c, --changes          like verbose but report only when a change is made
  -f, --silent, --quiet  suppress most error messages
  -v, --verbose          output a diagnostic for every file processed
      --no-preserve-root  do not treat '/' specially (the default)
      --preserve-root    fail to operate recursively on '/'
      --reference=RFILE  use RFILE's mode instead of MODE values
  -R, --recursive        change files and directories recursively
      --help     display this help and exit
      --version  output version information and exit

Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'.

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/chmod>
or available locally via: info '(coreutils) chmod invocation'

💡 说明:chmod 的选项比 chown/chgrp 少——没有 -h/--no-dereference(Linux 上 chmod 对软链默认就是改软链指向的目标,内核不存软链的权限位,软链恒显示 lrwxrwxrwx)。重头戏全在 MODE 语法上:rwx 之外还有大写 X(条件执行位)和 st(setuid/setgid/sticky),2.2 的 MODE 形式定义就是解谜钥匙。

2.3 版本确认

$ chmod --version | head -1
chmod (GNU coreutils) 8.32

三、参数逐项实测

3.1 数字模式:644/600/755 逐个对照

八进制数字每位对应一组 rwx(r=4、w=2、x=1,相加),三段依次是属主 u、属组 g、其他人 o。三个最常用值逐一实测:

$ chmod 644 /root/demo/chmod/f1.txt

$ ls -l /root/demo/chmod/f1.txt
-rw-r--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f1.txt

$ chmod 600 /root/demo/chmod/f1.txt

$ ls -l /root/demo/chmod/f1.txt
-rw------- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f1.txt

$ chmod 755 /root/demo/chmod/script.sh

$ ls -l /root/demo/chmod/script.sh
-rwxr-xr-x 1 root root 29 Oct 15 09:01 /root/demo/chmod/script.sh

$ /root/demo/chmod/script.sh
hello chmod

💡 要点:644(rw-r--r--)是普通文件的标配,600(rw-------)适合私钥等敏感文件,755(rwxr-xr-x)是脚本/可执行文件的标配——755 的 x 位加上后脚本真的能直接跑了(执行输出 hello chmod)。

3.2 符号模式:u+x、g-w、o=r 逐个叠加

符号模式的 three 要素:who(u/g/o/a)+ 操作符(+ 加、- 减、= 精确赋值)+ 权限字母。对 f2.txt 从 664 开始逐步叠加:

$ chmod 664 /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rw-rw-r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod u+x /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxrw-r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod g-w /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxr--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod o=r /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxr--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

💡 要点:u+x 只动属主位、g-w 只减属组 w、o=r 把其他人精确设成 r(原有权限全部覆盖)。= 是"赋值"不是"追加",o=r 之后 o 位只剩 r。

3.3 符号模式:a+r 与逗号组合 u+x,g-w

a 代表 ugo 全体;多个操作用逗号连写,从左到右依次执行:

$ chmod 600 /root/demo/chmod/f2.txt

$ chmod a+r /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rw-r--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod u+x,g-w /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxr--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

💡 要点:a+r 与 ugo+r 等价。逗号组合 u+x,g-w 一条命令完成两个动作,比写两条 chmod 少一半进程开销;注意逗号两侧不能有空格(有空格要整体加引号)。

3.4 裸 +x:省略 who 时受 umask 限制

who 省略(如 chmod +x FILE)时,行为是"对 a 生效,但 umask 屏蔽掉的位除外"。umask 0022 屏蔽的是 g/o 的 w 位(不含 x),所以裸 +x 三段都能加上 x:

$ chmod 644 /root/demo/chmod/f2.txt

$ chmod +x /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxr-xr-x 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

把 umask 换成 0333(屏蔽 g/o 的 w+x),同样从 644 开始裸 +x,结果完全不动:

$ umask 0333

$ chmod 644 /root/demo/chmod/f2.txt

$ chmod +x /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rw-r--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ umask 0022

$ chmod u+x /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxr--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod 644 /root/demo/chmod/f2.txt

💡 要点:这是符号模式最容易误会的细节——裸 +x 不等于 a+x:umask 0333 实测下 +x 一个位都没加上(g/o 的 x 被 umask 屏蔽),而显式 u+x 不受 umask 管辖照加。结论:脚本里想"人人可执行"就写 a+x 或干脆 755,裸 +x 的效果随 umask 漂移,不可靠。

3.5 = 赋值与空值清空

= 可以一次赋多段,也可以赋"空"来清空。u=rwx,g=rx,o=r 与 754 等价;u= 把属主位全部清零:

$ chmod 754 /root/demo/chmod/f2.txt

$ chmod u=rwx,g=rx,o=r /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
-rwxr-xr-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod u= /root/demo/chmod/f2.txt

$ ls -l /root/demo/chmod/f2.txt
----r-xr-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f2.txt

$ chmod u+rw /root/demo/chmod/f2.txt

💡 要点:u= 空赋值是"清空属主位"的简洁写法(等价 u-rwx)。u=rwx,g=rx,o=r 这种三段连写比 754 更自解释,脚本评审时一眼能看懂意图。

3.6 数字与符号混用报错

一个 MODE 元素要么纯八进制、要么纯符号,两者混写直接语法报错:

$ chmod 755+x /root/demo/chmod/f2.txt
chmod: invalid mode: '755+x'
Try 'chmod --help' for more information.

$ chmod u+x,755 /root/demo/chmod/f2.txt
chmod: invalid mode: 'u+x,755'
Try 'chmod --help' for more information.

💡 要点:报错信息 invalid mode 会把整个非法模式原样回显。逗号分隔的 MODE 里每个元素独立校验(u+x,755 里的 755 元素既不是合法符号也不是合法带前缀八进制)。要"数字+符号"混合的效果,拆成两条命令或用 u=rwx,g=rx,o=r 全符号写法。

3.7 --reference:参照另一个文件对齐权限

--reference=RFILE 把目标文件的权限位改成 RFILE 的现状。ref.txt 先设为 640 作参照:

$ chmod 640 /root/demo/chmod/ref.txt

$ ls -l /root/demo/chmod/ref.txt
-rw-r----- 1 root root 8 Oct 15 09:01 /root/demo/chmod/ref.txt

$ chmod 777 /root/demo/chmod/f3.txt

$ chmod --reference=/root/demo/chmod/ref.txt /root/demo/chmod/f3.txt

$ ls -l /root/demo/chmod/f3.txt
-rw-r----- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f3.txt

💡 要点:777 的 f3.txt 一键对齐成 640。注意 --reference 只对齐权限位(含 setuid/setgid/sticky),不含属主属组——后者是 chown/chgrp 的 --reference 干的活,三个命令的同名参数分工明确。

3.8 -c/-v/-f:变更输出与静默

-c 只报变化的条目(附新旧八进制与符号表示),无变化零输出;-v 无论是否变化都报;-f 静默错误:

$ chmod -c 700 /root/demo/chmod/f1.txt
mode of '/root/demo/chmod/f1.txt' changed from 0600 (rw-------) to 0700 (rwx------)

$ chmod -c 700 /root/demo/chmod/f1.txt

$ chmod -v 644 /root/demo/chmod/f1.txt
mode of '/root/demo/chmod/f1.txt' changed from 0700 (rwx------) to 0644 (rw-r--r--)

$ chmod -v 644 /root/demo/chmod/f1.txt
mode of '/root/demo/chmod/f1.txt' retained as 0644 (rw-r--r--)

$ chmod -f 0644 /root/demo/chmod/f1.txt

$ ls -l /root/demo/chmod/f1.txt
-rw-r--r-- 1 root root 11 Oct 15 09:01 /root/demo/chmod/f1.txt

💡 要点:chmod 的 -c/-v 输出比 chown 的信息量大——同时给出四位八进制(0600)与符号(rw-------)两种表示,审计日志更直观。changed/retained 的区分与 chown 一致。

3.9 -R 递归

-R 对目录树逐层修改。-R 700 会把目录和文件一视同仁地设为 700:

$ chmod -R 700 /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rwx------ 1 root root    6 Oct 15 09:01 f.txt
-rwx------ 1 root root   27 Oct 15 09:01 run.sh
drwx------ 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

$ chmod -R u+rwX /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rwx------ 1 root root    6 Oct 15 09:01 f.txt
-rwx------ 1 root root   27 Oct 15 09:01 run.sh
drwx------ 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

💡 要点:数字模式 -R 700 对文件也给了 x 位(f.txt/run.sh 变 -rwx------),这不是发布权限想要的。第二条 chmod -R u+rwX 后没有任何变化——因为 u 的 rwX 都已满足(X 对"目录或已有 x 的文件"才加 x,这两个文件本来就有 x)。想要"目录 755、文件 644"的精确效果,看 3.10 的大写 X 与 find 两种方案。

3.10 大写 X:目录加 x、无执行位的文件不加,与 -R 的黄金组合

大写 X 的语义(帮助 MODE 定义里的 [rwxXst] 之一):仅当目标是目录,或已有任一执行位时,才设置 x。先用 a-x 把整棵树(目录+文件)的执行位全部剥掉——注意目录也变成了 drw-------(无 x 的目录连 cd 都进不去):

$ chmod -R a-x /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw------- 1 root root    6 Oct 15 09:01 f.txt
-rw------- 1 root root   27 Oct 15 09:01 run.sh
drw------- 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

$ chmod -R a+rX /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw-r--r-- 1 root root    6 Oct 15 09:01 f.txt
-rw-r--r-- 1 root root   27 Oct 15 09:01 run.sh
drwxr-xr-x 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

$ chmod -R a-X /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw-r--r-- 1 root root    6 Oct 15 09:01 f.txt
-rw-r--r-- 1 root root   27 Oct 15 09:01 run.sh
drw-r--r-- 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

同一目标用 find 按类型分别处理,结果可精确控制:

$ chmod -R 666 /root/demo/chmod/d1

$ find /root/demo/chmod/d1 -type d -exec chmod 755 {} +

$ find /root/demo/chmod/d1 -type f -exec chmod 644 {} +

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw-r--r-- 1 root root    6 Oct 15 09:01 f.txt
-rw-r--r-- 1 root root   27 Oct 15 09:01 run.sh
drwxr-xr-x 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

💡 要点:三组对照把 X 的语义钉死——(1) a-x 一视同仁剥光,目录失去搜索权;(2) a+rX 是发布权限神器:目录 755、文件 644,一条命令搞定;(3) a-X 只剥"目录和已有 x 的文件"的 x,纯文本文件不受影响(实测 f.txt 仍 644)。find 方案更可控但两条命令;-R a+rX(或 u=rwX,go=rX,见 4.4)是单命令方案。

3.11 chmod 不受 umask 影响

umask 只作用于新建文件的默认权限,chmod 永远不看 umask。umask 077 下新建文件是 600,随后 chmod 755 照样把权限推到 755:

$ umask 077

$ touch /root/demo/chmod/umask_new.txt

$ ls -l /root/demo/chmod/umask_new.txt
-rw------- 1 root root 0 Oct 15 09:01 /root/demo/chmod/umask_new.txt

$ chmod 755 /root/demo/chmod/umask_new.txt

$ ls -l /root/demo/chmod/umask_new.txt
-rwxr-xr-x 1 root root 0 Oct 15 09:01 /root/demo/chmod/umask_new.txt

$ chmod -R 755 /root/demo/chmod/d1

$ ls -ld /root/demo/chmod/d1
drwxr-xr-x 3 root root 4096 Oct 15 09:01 /root/demo/chmod/d1

$ umask 0022

💡 要点:umask 是"创建掩码",chmod 是"显式设置",两者作用时机不同、互不覆盖。-R 755 在 umask 077 下照样给足 755——所以"chmod 完发现权限没变,是不是 umask 挡了"这类怀疑可以排除(真正要看的是 3.4 裸 +x 那种符号模式的 umask 敏感行为)。

3.12 特殊权限位:setuid 4755 / setgid 2755 / 6755

八进制第四位(最高位)是特殊权限:4=setuid、2=setgid、1=sticky。对可执行二进制逐个设置并观察 ls -l:

$ cp /bin/id /root/demo/chmod/suid_id

$ chmod 4755 /root/demo/chmod/suid_id

$ ls -l /root/demo/chmod/suid_id
-rwsr-xr-x 1 root root 39432 Oct 15 09:01 /root/demo/chmod/suid_id

$ chmod 6755 /root/demo/chmod/suid_id

$ ls -l /root/demo/chmod/suid_id
-rwsr-sr-x 1 root root 39432 Oct 15 09:01 /root/demo/chmod/suid_id

$ chmod 755 /root/demo/chmod/suid_id

💡 要点:setuid 让属主的 x 位显示为 s(-rws),setgid 让属组的 x 位显示为 s(-rws)。符号写法 u+s/g+s 等价。setuid 的实际效果——进程执行瞬间 euid 变成文件属主——见 3.13 的运行时实测。

3.13 setuid 运行时实测:二进制生效、shell 脚本无效

把 /bin/id 复制为 4755 的 root 属主二进制,普通用户 u4d1 执行时 euid 变成 root;去掉 setuid 后恢复:

$ cp /bin/id /tmp/u4d-demo/suid_id

$ chmod 4755 /tmp/u4d-demo/suid_id

$ ls -l /tmp/u4d-demo/suid_id
-rwsr-xr-x 1 root root 39432 Oct 15 09:01 /tmp/u4d-demo/suid_id

$ su u4d1 -c '/tmp/u4d-demo/suid_id'
uid=1000(u4d1) gid=1000(u4d1) euid=0(root) groups=1000(u4d1),1001(u4dgrp1)

$ chmod 755 /tmp/u4d-demo/suid_id

$ su u4d1 -c '/tmp/u4d-demo/suid_id'
uid=1000(u4d1) gid=1000(u4d1) groups=1000(u4d1),1001(u4dgrp1)

同样 4755 的 shell 脚本,u4d1 执行后 euid 仍是 u4d1——内核对脚本上的 setuid 直接忽略:

$ printf '#!/bin/bash\nid\n' > /tmp/u4d-demo/suid_script.sh

$ chmod 4755 /tmp/u4d-demo/suid_script.sh

$ ls -l /tmp/u4d-demo/suid_script.sh
-rwsr-xr-x 1 root root 15 Oct 15 09:01 /tmp/u4d-demo/suid_script.sh

$ su u4d1 -c '/tmp/u4d-demo/suid_script.sh'
uid=1000(u4d1) gid=1000(u4d1) groups=1000(u4d1),1001(u4dgrp1)

💡 要点:setuid 只对二进制生效(euid=0(root) 实锤),shell 脚本带 suid 无效——因为脚本的真实执行体是 /bin/bash 解释器,Linux 内核出于安全明确不追随脚本的解释器提权。想让脚本"以 root 身份跑",要么 sudo,要么写个 suid 二进制包装器。顺带注意:3.12 的 -rws 位挂在 4755 的 suid_id 上时,对照 3.13 可知这个位是真实生效的,不只是显示。

3.14 setgid 目录 2775:新建文件自动继承目录属组

目录挂 setgid(g+s 或八进制 2775)后,在其中新建的文件/子目录属组自动继承目录属组,而不是创建者的主组:

$ mkdir -p /root/demo/chmod/sgdir

$ chgrp u4dgrp1 /root/demo/chmod/sgdir

$ chmod 2775 /root/demo/chmod/sgdir

$ ls -ld /root/demo/chmod/sgdir
drwxrwsr-x 2 root u4dgrp1 4096 Oct 15 09:01 /root/demo/chmod/sgdir

$ touch /root/demo/chmod/sgdir/rootfile

$ ls -l /root/demo/chmod/sgdir/rootfile
-rw-r--r-- 1 root u4dgrp1 0 Oct 15 09:01 /root/demo/chmod/sgdir/rootfile

普通用户创建同样继承(u4d1 主组是 u4d1,新文件属组却是 u4dgrp1):

$ su u4d1 -c 'touch /tmp/u4d-demo/sgdir/newfile'

$ ls -l /tmp/u4d-demo/sgdir/newfile
-rw-rw-r-- 1 u4d1 u4dgrp1 0 Oct 15 09:01 /tmp/u4d-demo/sgdir/newfile

一个意外发现:四位八进制的前导 0 不能清除 setgid——chmod 0775 后目录仍是 drwxrwsr-x,新建文件照样继承;只有符号模式 g-s 才真的摘掉:

$ chmod 2775 /tmp/u4d-demo/sgdir

$ ls -ld /tmp/u4d-demo/sgdir
drwxrwsr-x 2 root u4dgrp1 4096 Oct 15 09:04 /tmp/u4d-demo/sgdir

$ chmod 0775 /tmp/u4d-demo/sgdir

$ ls -ld /tmp/u4d-demo/sgdir
drwxrwsr-x 2 root u4dgrp1 4096 Oct 15 09:04 /tmp/u4d-demo/sgdir

$ chmod g-s /tmp/u4d-demo/sgdir

$ ls -ld /tmp/u4d-demo/sgdir
drwxrwxr-x 2 root u4dgrp1 4096 Oct 15 09:04 /tmp/u4d-demo/sgdir

$ su u4d1 -c 'touch /tmp/u4d-demo/sgdir/afters'

$ ls -l /tmp/u4d-demo/sgdir/afters
-rw-rw-r-- 1 u4d1 u4d1 0 Oct 15 09:04 /tmp/u4d-demo/sgdir/afters

💡 要点:三个实测结论——(1) setgid 目录是团队协作目录的标准姿势:属组固定,成员新建的文件天然归组,配合 2775 或 775+g+s 使用;(2) 八进制模式只"设置"权限位,前导 0 不等于"清除特殊位"(0775 与 775 等价,s 位原样保留,实测打脸直觉);(3) 想摘特殊位必须用符号 g-s/u-s/o-t——摘掉后新建文件属组立刻回到创建者主组(afters 属组 u4d1)。

3.15 sticky 1777:删除保护与 /tmp 对照

目录挂 sticky(+t 或八进制 1777)后,任何人可写,但只能删除/改名自己的文件。先看权限位形态并与系统 /tmp 对照:

$ mkdir -p /root/demo/chmod/sticky_dir

$ chmod 1777 /root/demo/chmod/sticky_dir

$ ls -ld /root/demo/chmod/sticky_dir
drwxrwxrwt 2 root root 4096 Oct 15 09:01 /root/demo/chmod/sticky_dir

$ ls -ld /tmp
drwxrwxrwt 1 root root 4096 Oct 15 09:01 /tmp

u4d1 无法删除 u4d2 在 sticky 目录里的文件,但能删自己的;对照组:普通 777 目录里 u4d2 一句话删掉了 u4d1 的文件:

$ su u4d1 -c 'touch /tmp/u4d-demo/sticky777/f_u4d1'

$ su u4d2 -c 'touch /tmp/u4d-demo/sticky777/f_u4d2'

$ su u4d1 -c 'rm /tmp/u4d-demo/sticky777/f_u4d2'
rm: cannot remove '/tmp/u4d-demo/sticky777/f_u4d2': Operation not permitted

$ su u4d1 -c 'rm /tmp/u4d-demo/sticky777/f_u4d1'

$ su u4d1 -c 'touch /tmp/u4d-demo/plain777/a_u4d1'

$ su u4d2 -c 'rm /tmp/u4d-demo/plain777/a_u4d1'

$ ls -la /tmp/u4d-demo/plain777 /tmp/u4d-demo/sticky777
/tmp/u4d-demo/plain777:
total 8
drwxrwxrwx 2 root root 4096 Oct 15 09:01 .
drwxr-xr-x 7 root root 4096 Oct 15 09:01 ..

/tmp/u4d-demo/sticky777:
total 8
drwxrwxrwt 2 root root 4096 Oct 15 09:01 .
drwxr-xr-x 7 root root 4096 Oct 15 09:01 ..
-rw-rw-r-- 1 u4d2 u4d2    0 Oct 15 09:01 f_u4d2

💡 要点:sticky 位让 others 的 x 位显示为 t(drwxrwxrwt),/tmp 正是这个权限——这就是为什么人人可写 /tmp 却删不了别人的文件。对照实验里普通 777 目录的 a_u4d1 被 u4d2 删得干干净净(ls -la 只剩目录本身),而 sticky777 里 f_u4d2 安然无恙。共享临时目录、上传目录,1777 是安全标配。

3.16 r 与 x 对目录的真实含义(含 chmod 000 与 root 豁免)

目录的 r=列出文件名,x=进入目录与访问内部文件的"钥匙"。444 目录(有 r 无 x):能 ls 列名,不能 cd、不能读内容;111 目录(有 x 无 r):不能 ls,但能 cd 进去按名访问:

$ chmod 444 /tmp/u4d-demo/r_only

$ chmod 111 /tmp/u4d-demo/x_only

$ ls -ld /tmp/u4d-demo/r_only /tmp/u4d-demo/x_only
dr--r--r-- 2 u4d1 root 4096 Oct 15 09:01 /tmp/u4d-demo/r_only
d--x--x--x 2 u4d1 root 4096 Oct 15 09:01 /tmp/u4d-demo/x_only

$ su u4d1 -c 'ls /tmp/u4d-demo/r_only'
inner.txt

$ su u4d1 -c 'ls -l /tmp/u4d-demo/r_only'
ls: cannot access '/tmp/u4d-demo/r_only/inner.txt': Permission denied
total 0
-????????? ? ? ? ?            ? inner.txt

$ su u4d1 -c 'cd /tmp/u4d-demo/r_only'
sh: 1: cd: can't cd to /tmp/u4d-demo/r_only

$ su u4d1 -c 'cat /tmp/u4d-demo/r_only/inner.txt'
cat: /tmp/u4d-demo/r_only/inner.txt: Permission denied

$ su u4d1 -c 'ls /tmp/u4d-demo/x_only'
ls: cannot open directory '/tmp/u4d-demo/x_only': Permission denied

$ su u4d1 -c 'cd /tmp/u4d-demo/x_only && cat inner.txt'
secret

把文件权限设为 000(----------):root 照读不误,普通用户被拒:

$ chmod 000 /tmp/u4d-demo/zero.txt

$ ls -l /tmp/u4d-demo/zero.txt
---------- 1 root root 10 Oct 15 09:01 /tmp/u4d-demo/zero.txt

$ cat /tmp/u4d-demo/zero.txt
topsecret

$ su u4d1 -c 'cat /tmp/u4d-demo/zero.txt'
cat: /tmp/u4d-demo/zero.txt: Permission denied

💡 要点:三个实测结论——(1) 目录无 x 时 cd 直接 Permission denied,ls -l 里的文件全是 ??????(能看名不能看属性);(2) 有 x 无 r 时"盲飞":ls 报 Permission denied,但 cd 进去+按已知文件名 cat 畅通(inner.txt 的 secret 读出来了);(3) chmod 000 后 root 照样可读(root 的访问不经过普通权限位检查),普通用户则彻底没门——"000 就安全"只是防同事不防 root。

3.17 技术拓展:chmod vs umask vs setfacl

Linux 上"控制文件访问权限"有三层主流手段,定位各不相同——

手段作用对象作用时机粒度典型场景
chmod已有文件的权限位显式执行时属主/属组/其他人 三段修正存量文件权限、给脚本加 x
umask新建文件的默认权限创建瞬间自动扣除三段掩码登录 shell/服务进程的默认安全基线
setfacl (ACL)单文件/目录的访问控制列表显式执行时任意指定用户或组三段权限位不够用时的精细授权

💡 技术拓展:umask 管"出生"(如 umask 077 让新文件只有属主可读写)、chmod 管"后天修整"、ACL 管"特批"(给特定用户开小灶,getfacl 查看、setfacl -m u:u4d1:rx 目录 授权,挂载时可能需要 acl 选项)。三者叠加生效:ACL 存在时 ls -l 末尾出现 + 号,此时传统三段位的 others 段被 ACL mask 修正。本文主题是 chmod,ACL 只点到为止(详见 setfacl 专题篇);服务部署场景的黄金组合是"umask 定基线 + chmod 修存量 + setfacl 补特例"。

四、实战示例

4.1 发布目录一键定权:目录 755、文件 644

$ chmod -R a+rX /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw-r--r-- 1 root root    6 Oct 15 09:01 f.txt
-rw-r--r-- 1 root root   27 Oct 15 09:01 run.sh
drwxr-xr-x 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

💡 说明:-R a+rX 是单命令方案——目录拿到 r+x(可进可列),纯文件只拿 r 不拿 x。等价的两命令方案见 3.10 的 find(-type d -exec chmod 755 + -type f -exec chmod 644),需要更细控制时用它。

4.2 误 chmod -R 666 后的自救

$ chmod -R 666 /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw-rw-rw- 1 root root    6 Oct 15 09:01 f.txt
-rw-rw-rw- 1 root root   27 Oct 15 09:01 run.sh
drw-rw-rw- 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

$ chmod -R u=rwX,go=rX /root/demo/chmod/d1

$ ls -lR /root/demo/chmod/d1
/root/demo/chmod/d1:
total 12
-rw-r--r-- 1 root root    6 Oct 15 09:01 f.txt
-rw-r--r-- 1 root root   27 Oct 15 09:01 run.sh
drwxr-xr-x 2 root root 4096 Oct 15 09:01 sub

/root/demo/chmod/d1/sub:
total 0

💡 说明:-R 666 把目录的 x 也抹了(drw-rw-rw-,cd 进不去)。自救一条命令:u=rwX,go=rX——X 只给目录恢复 x,文件保持 644,一步回到发布状态。该场景下 a+rX(先补齐 r 再按需 x)同样有效,但会把文件放宽到 644 之外不会收回原有的 w(666 文件仍是 666),=rwX 赋值式才连"收回多余权限"一起做。

4.3 团队协作目录:setgid 继承属组

$ chgrp u4dgrp1 /root/demo/chmod/sgdir

$ chmod 2775 /root/demo/chmod/sgdir

$ ls -ld /root/demo/chmod/sgdir
drwxrwsr-x 2 root u4dgrp1 4096 Oct 15 09:01 /root/demo/chmod/sgdir

$ touch /root/demo/chmod/sgdir/rootfile

$ ls -l /root/demo/chmod/sgdir/rootfile
-rw-r--r-- 1 root u4dgrp1 0 Oct 15 09:01 /root/demo/chmod/sgdir/rootfile

💡 说明:2775 = 属组可写 + setgid。任何成员(哪怕属组不是主组)在此新建的文件自动归 u4dgrp1,配合 chmod g+s 常态保持,摘除必须用 g-s(3.14 实测 0775 摘不掉)。

4.4 共享临时区:sticky 1777

$ chmod 1777 /root/demo/chmod/sticky_dir

$ ls -ld /root/demo/chmod/sticky_dir
drwxrwxrwt 2 root root 4096 Oct 15 09:01 /root/demo/chmod/sticky_dir

💡 说明:与 /tmp 同款权限。人人可写但各删各的(删除保护实测见 3.15),上传目录、交换目录的标准安全姿势。

4.5 变更审计:-c 留痕

$ chmod -c 700 /root/demo/chmod/f1.txt
mode of '/root/demo/chmod/f1.txt' changed from 0600 (rw-------) to 0700 (rwx------)

$ chmod -c 700 /root/demo/chmod/f1.txt

💡 说明:changed 行带新旧八进制与符号双表示,第二次执行零输出即已到位。chmod -R -c 644 大目录 2>&1 | tee 权限审计.log 就是现成的变更记录。

五、注意事项与常见问题

Q1:chmod +x 和 chmod a+x 有区别吗? 有。裸 +x 省略 who 时受 umask 屏蔽位影响——umask 0333 实测下 g/o 的 x 加不上,文件纹丝不动;a+x 显式指定不受 umask 管辖。给脚本加执行位想"必成",用 a+x 或数字 755(见 3.4 对照实测)。

Q2:chmod -R 777 会把 setuid/setgid/sticky 清掉吗? 三位数字(777)不动特殊位,四位数字(0777)理论上指明特殊位为 0——但实测发现 chmod 0775 并没有摘掉目录的 setgid(3.14),前导 0 不可依赖。想明确摘特殊位就用符号模式 u-s/g-s/o-t。反过来:chmod -R 777 也不清除已存在的特殊位,处理前先 find dir -perm /7000 排查。

Q3:为什么脚本 chmod 4755 后普通用户执行没有提权? setuid 对 shell 脚本无效(内核安全设计),只有二进制生效(3.13 的 euid=0 对照实测)。脚本要提权走 sudo。

Q4:cd 目录报 Permission denied,ls 却能看到文件名? 目录有 r 无 x(如 444):能列名,不能进、不能 stat(ls -l 全是 ??????,3.16 实测)。目录需要 x 才能作为路径穿越。修复:给目录补 x(chmod u+x 目录)。

Q5:chmod 之后权限怎么没变? 排除三种情况:(1) 符号裸 +x 被umask 屏蔽(3.4);(2) 用了 = 但写错段(o=r 只管 o 段);(3) 文件系统不支持(如某些网络文件系统挂载时忽略权限)。另外 root 对权限位"豁免",ls -l 显示与实际访问能力是两回事(000 文件 root 照读,3.16)。

Q6:-R 递归 chmod 会不会穿透软链改到目标文件? chmod -R 默认不跟随命令行之外的软链,且 Linux 根本不给软链存权限位(恒 lrwxrwxrwx)。对软链参数,chmod 默认跟随改目标(与 chown 相同的方向);GNU chmod 没有 -h 选项,需要"跳过软链"用 find -type f 限定。

Q7:umask 077 后 chmod 755 还会生效吗? 生效。umask 只影响新建文件的默认权限(touch 出来 600),chmod 显式设置永远不看 umask(3.11 实测 umask 077 下 chmod -R 755 照样推到 755)。

Q8:8 进制 4 位数怎么读? 从左到右:特殊位(4 suid/2 sgid/1 sticky 相加)→ 属主 → 属组 → 其他人。4755 = suid + 755;2775 = sgid + 775;1777 = sticky + 777。三位数时特殊位保持原样不清除。

六、参考链接