chown 命令详解与参数实测(Ubuntu 22.04)
GNU chown 参数实测:owner:group 与点号语法、数字 UID/GID、-c/-v/-f、-h 软链解引用、-R 与 -H/-L/-P 遍历语义、--from 条件变更、--reference 及 invalid user/Operation not permitted 错误形态,Ubuntu 22.04 实测。
chown 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 chown 的每一个常用参数——修改属主、
owner:group同时修改、只改属组、数字 UID/GID、-c/-v/-f输出控制、-h软链本身与默认解引用、-R递归及-H/-L/-P三种遍历语义、--from条件变更、--reference参照文件,以及 invalid user、Operation not permitted 等错误形态,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
本篇所有命令与回显均来自同一台 Ubuntu 22.04 服务器的实测,演示数据统一放在 /root/demo/chown/ 下,另在 /tmp/u4d-demo/ 做普通用户权限实验。读者在任意 Ubuntu 22.04 机器上按相同步骤均可复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- chown 版本:GNU coreutils 8.32(
chown --version实测) - chown 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/chown/(root 身份操作);/tmp/u4d-demo/(普通用户实验) - 测试用户与组:
u4d1(uid=1000,主组 u4d1)、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 语法与测试数据
chown 的语法共两种形态(摘自帮助 Usage 行):chown [OPTION]... [OWNER][:[GROUP]] FILE... 按名字/数字修改属主属组,chown [OPTION]... --reference=RFILE FILE... 参照另一个文件对齐属主属组。属主部分(OWNER)与属组部分(GROUP)都可以省略,省略即"不修改"。
准备演示数据——两个普通文件、一棵带子目录与树内软链的目录树、两个软链(一个指向文件、一个指向目录):
mkdir -p /root/demo/chown/tree/sub1 /root/demo/chown/tree/sub2
echo 'chown demo a' > /root/demo/chown/a.txt
echo 'chown demo b' > /root/demo/chown/b.txt
echo 'ref content' > /root/demo/chown/ref.txt
echo inner1 > /root/demo/chown/tree/sub1/inner1.txt
echo inner2 > /root/demo/chown/tree/sub2/inner2.txt
ln -s ../sub2/inner2.txt /root/demo/chown/tree/sub1/link_inner2
ln -s /root/demo/chown/a.txt /root/demo/chown/link_a
ln -s /root/demo/chown/tree /root/demo/chown/link_tree
验证数据就绪:
$ ls -l /root/demo/chown
total 16
-rw-r--r-- 1 root root 13 Oct 15 08:43 a.txt
-rw-r--r-- 1 root root 13 Oct 15 08:43 b.txt
lrwxrwxrwx 1 root root 22 Oct 15 08:43 link_a -> /root/demo/chown/a.txt
lrwxrwxrwx 1 root root 21 Oct 15 08:43 link_tree -> /root/demo/chown/tree
-rw-r--r-- 1 root root 12 Oct 15 08:43 ref.txt
drwxr-xr-x 4 root root 4096 Oct 15 08:43 tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 root root 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 root root 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 root root 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 root root 7 Oct 15 08:43 inner2.txt
确认测试用户的 uid/gid,后文所有属主变化都围绕它们观察:
$ id u4d1
uid=1000(u4d1) gid=1000(u4d1) groups=1000(u4d1)
$ id u4d2
uid=1001(u4d2) gid=1003(u4d2) groups=1003(u4d2),1001(u4dgrp1),1002(u4dgrp2)
2.2 --help 全量输出
chown --help 输出全部参数清单(实测原样保留,可作为参数速查):
Usage: chown [OPTION]... [OWNER][:[GROUP]] FILE...
or: chown [OPTION]... --reference=RFILE FILE...
Change the owner and/or group of each FILE to OWNER and/or GROUP.
With --reference, change the owner and group of each FILE to those 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
--dereference affect the referent of each symbolic link (this is
the default), rather than the symbolic link itself
-h, --no-dereference affect symbolic links instead of any referenced file
(useful only on systems that can change the
ownership of a symlink)
--from=CURRENT_OWNER:CURRENT_GROUP
change the owner and/or group of each file only if
its current owner and/or group match those specified
here. Either may be omitted, in which case a match
is not required for the omitted attribute
--no-preserve-root do not treat '/' specially (the default)
--preserve-root fail to operate recursively on '/'
--reference=RFILE use RFILE's owner and group rather than
specifying OWNER:GROUP values
-R, --recursive operate on files and directories recursively
The following options modify how a hierarchy is traversed when the -R
option is also specified. If more than one is specified, only the final
one takes effect.
-H if a command line argument is a symbolic link
to a directory, traverse it
-L traverse every symbolic link to a directory
encountered
-P do not traverse any symbolic links (default)
--help display this help and exit
--version output version information and exit
Owner is unchanged if missing. Group is unchanged if missing, but changed
to login group if implied by a ':' following a symbolic OWNER.
OWNER and GROUP may be numeric as well as symbolic.
Examples:
chown root /u Change the owner of /u to "root".
chown root:staff /u Likewise, but also change its group to "staff".
chown -hR root /u Change the owner of /u and subfiles to "root".
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/chown>
or available locally via: info '(coreutils) chown invocation'
💡 说明:帮助末尾两段最关键——"Owner is unchanged if missing"(省略属主就不改属主)、"changed to login group if implied by a ':' following a symbolic OWNER"(
owner:冒号结尾会把属组重置为该属主的登录组)。-H/-L/-P只在配合-R时生效,多个同时给出时只有最后一个生效。
2.3 版本确认
$ chown --version | head -1
chown (GNU coreutils) 8.32
三、参数逐项实测
3.1 基本用法:只改属主
命令格式为 chown OWNER FILE。只给属主不给属组时,属组保持不变——这是帮助文本 "Owner is unchanged if missing" 的直接体现。先把 a.txt 的属主改为 u4d1,再改为 u4d2,ls -l 第三、四列分别为属主与属组:
$ chown u4d1 /root/demo/chown/a.txt
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 u4d1 root 13 Oct 15 08:43 /root/demo/chown/a.txt
$ chown u4d2 /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d2 root 13 Oct 15 08:43 /root/demo/chown/b.txt
💡 要点:改属主时属组纹丝不动(两次实测都是 root)。修改属主需要内核能力 CAP_CHOWN,普通用户连自己的文件都不能把属主让给别人(3.14 有实测)。
3.2 owner:group 同时修改属主与属组
冒号分隔属主与属组,一次调用同时改两个属性:
$ chown u4d2:u4dgrp1 /root/demo/chown/a.txt
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 u4d2 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/a.txt
属主、属组一列三态:u4d2(只改属主)、u4d2:u4dgrp1(都改)、:u4dgrp1(只改属组,见 3.4)。
另一个实测点是"冒号结尾":chown u4d1: FILE 中冒号后组名为空,按帮助文本的规则,属组会被重置为属主 u4d1 的登录组(u4d1 用户的主组恰好也叫 u4d1)。实测 a.txt 原属组是 u4dgrp1:
$ chown u4d1: /root/demo/chown/a.txt
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 u4d1 u4d1 13 Oct 15 08:43 /root/demo/chown/a.txt
💡 要点:
owner:与owner不同——前者把属组重置为属主的登录组,后者完全不碰属组。清理"属主换了、组还挂在别人头上"的残留时,chown user:一条顶两条。
3.3 老式点号语法 owner.group
chown 兼容 BSD 时代的点号分隔符 owner.group,行为与冒号完全一致:
$ chown u4d2.u4dgrp2 /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d2 u4dgrp2 13 Oct 15 08:43 /root/demo/chown/b.txt
点号同样支持 .group 只改属组:
$ chown .u4dgrp2 /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d2 u4dgrp2 13 Oct 15 08:43 /root/demo/chown/b.txt
💡 要点:点号是历史遗留语法。现代系统允许用户名本身含点号(如
john.doe),此时chown john.doe FILE会被解析成属主 john、属组 doe 而不是属主 john.doe,脚本里应统一用冒号,避免歧义。
3.4 :group 只改属组不改属主
属主位置留空、冒号紧跟组名,即可只改属组。a.txt 当前属主是 u4d2,改完仍是 u4d2:
$ chown :u4dgrp1 /root/demo/chown/a.txt
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 u4d2 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/a.txt
💡 要点:
:group与chgrp等价(chgrp 篇有专门实测)。区别在于 chown 还允许普通用户在"目标组是自己所属组"时改组,而改属主永远只有 root 能做。要复用既有文件的属组又不想多敲一条命令时,chown --reference(3.13)更省事。
3.5 数字 UID/GID
OWNER 与 GROUP 都可以直接写数字(帮助原文 "OWNER and GROUP may be numeric as well as symbolic")。数字形式绕过名称解析:系统里不存在对应名称也照改不误。用 ls -ln(以数字显示属主属组)验证:
$ chown 1001:1002 /root/demo/chown/a.txt
$ ls -ln /root/demo/chown/a.txt
-rw-r--r-- 1 1001 1002 13 Oct 15 08:43 /root/demo/chown/a.txt
$ chown 65534 /root/demo/chown/b.txt
$ ls -ln /root/demo/chown/b.txt
-rw-r--r-- 1 65534 1002 13 Oct 15 08:43 /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 nobody u4dgrp2 13 Oct 15 08:43 /root/demo/chown/b.txt
💡 要点:
ls -l与ls -ln对照可以看出 65534 就是 nobody。数字形式在以下场景不可替代:批量迁移数据前目标用户还没建(先按数字把属主设好)、UID 在两台机器间不一致需要人工指定、name resolution 故障时的兜底。注意chown 100 FILE会把属主设为 uid=100 而不是名为"100"的用户——想用名为纯数字的用户名只能写chown 100: FILE加冒号消除歧义(coreutils 官方文档的建议做法)。
3.6 -c/--changes:只输出发生变化的条目
-c 只在属性真的变化时打印一行。a.txt 属主已是 u4d1 时再执行 chown -c u4d1,零输出;先把属主改成 u4d2 再改回 u4d1,changed 行立刻出现:
$ chown -c u4d1 /root/demo/chown/a.txt
$ chown -c u4d1 /root/demo/chown/a.txt
$ chown u4d2 /root/demo/chown/a.txt
$ chown -c u4d1 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d2 to u4d1
$ chown -c u4d1 /root/demo/chown/a.txt
💡 要点:
-c的价值在脚本里——批量修改几千个文件时,只打印真正动过的条目,输出干净且可直接进日志审计。"无变化零输出、有变化一行"也常被用作 shell 判断依据。
3.7 -v/--verbose:无论是否变化都输出
-v 对每个处理的文件都打印诊断行,变化了输出 changed,没变化输出 retained:
$ chown -v u4d2 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d1 to u4d2
$ chown -v u4d2 /root/demo/chown/a.txt
ownership of '/root/demo/chown/a.txt' retained as u4d2
💡 要点:retained(保持)与 changed(变化)两种措辞对应"属性相同"与"属性不同"两种结果。配合
-R时-v会逐文件刷屏,排查"到底改了哪些路径"时非常有用。
3.8 -f/--silent:抑制运行期错误消息
-f 抑制大多数错误消息。但它抑制的是"运行期失败"(如权限不够),对"参数解析失败"(用户/组不存在)无效——invalid user 依旧照报:
$ chown -f nosuchuser /root/demo/chown/a.txt
chown: invalid user: 'nosuchuser'
而普通用户改属主的 Operation not permitted 属于运行期错误,-f 可以完全静默(实测见 3.14)。
💡 要点:
-f典型用途是批量chown -R -f时忽略个别无权限路径的报错噪音,让脚本输出只关心最终结果。但也要小心:静默后失败只有退出码非零可判,脚本里建议同时检查$?。
3.9 -h/--no-dereference:改软链本身;默认解引用改目标
link_a 是指向 a.txt 的软链。不加 -h 时 chown 默认解引用(--dereference),改的是 a.txt;软链本身的属主纹丝不动:
$ chown u4d2 /root/demo/chown/link_a
$ ls -l /root/demo/chown/link_a /root/demo/chown/a.txt
-rw-r--r-- 1 u4d2 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/a.txt
lrwxrwxrwx 1 root root 22 Oct 15 08:43 /root/demo/chown/link_a -> /root/demo/chown/a.txt
加 -h 后作用于软链本身,a.txt 反而没动:
$ chown -h u4d1 /root/demo/chown/link_a
$ ls -l /root/demo/chown/link_a /root/demo/chown/a.txt
-rw-r--r-- 1 u4d2 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/a.txt
lrwxrwxrwx 1 u4d1 root 22 Oct 15 08:43 /root/demo/chown/link_a -> /root/demo/chown/a.txt
💡 要点:软链属主在 Linux 上对"谁能改这个链接本身"有意义(受父目录写权限约束),对访问目标文件不起作用,但备份/同步工具(rsync -a、tar)会保留它,属主错乱会让恢复出来的文件权限校验失败。帮助文本也注明
-h只在"系统能改软链属主"的内核上有意义,Linux 从 2.1.126 起支持。
3.10 -R/--recursive:递归修改整棵目录树
-R 对目录逐层下钻,把目录、子目录、目录内所有文件(含树内软链本身)一并修改。先复位为 root,再一次性交给 u4d1:u4dgrp1:
$ chown -R root:root /root/demo/chown/tree
$ chown -R u4d1:u4dgrp1 /root/demo/chown/tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 u4d1 u4dgrp1 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner2.txt
💡 要点:注意 link_inner2 这条软链——
ls -l显示它自身属主也变成了 u4d1 u4dgrp1,说明-R遍历时对"不跟随"的树内软链,是把软链本身当文件对象改的(跟随与否由 -H/-L/-P 决定,见 3.11)。实战中/home迁移用户、/var/www交给运行账户,都是-R的高频场景。
3.11 -H/-L/-P 与 -R 的组合遍历语义
帮助文本明确:这三个选项只在 -R 下有意义,同时给出时只有最后一个生效。语义对照:
| 选项 | 遍历语义 | 对命令行参数中的软链 | 对遍历途中遇到的软链 |
|---|---|---|---|
| -P(默认) | 不跟随任何软链 | 只改软链本身 | 只改软链本身 |
| -H | 仅命令行参数解引用 | 跟随并进入其指向的目录树 | 不跟随,只改软链本身 |
| -L | 全部解引用 | 跟随 | 跟随,指向目录就下钻 |
准备状态:link_tree 是指向 tree 的命令行软链,tree 内部还有一条 link_inner2 软链。三个实验之间都用 chown -R root:root 复位。
实验一,默认(等价 -P):只改了 link_tree 软链本身,tree 内部纹丝不动:
$ chown -R root:root /root/demo/chown/tree /root/demo/chown/link_tree /root/demo/chown/a.txt
$ chown -R u4d2:u4dgrp2 /root/demo/chown/link_tree
$ ls -l /root/demo/chown/link_tree
lrwxrwxrwx 1 u4d2 u4dgrp2 21 Oct 15 08:43 /root/demo/chown/link_tree -> /root/demo/chown/tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 root root 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 root root 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 root root 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 root root 7 Oct 15 08:43 inner2.txt
实验二,-H:命令行上的 link_tree 被解引用,整棵 tree 被改;但树内的 link_inner2 没有被跟随——它本身保持 root,而它指向的 inner2.txt 是通过目录遍历改到的:
$ chown -R root:root /root/demo/chown/tree /root/demo/chown/link_tree
$ chown -R -H u4d1:u4dgrp1 /root/demo/chown/link_tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner2.txt
$ ls -l /root/demo/chown/tree/sub1/link_inner2
lrwxrwxrwx 1 root root 18 Oct 15 08:43 /root/demo/chown/tree/sub1/link_inner2 -> ../sub2/inner2.txt
$ ls -l /root/demo/chown/tree/sub2/inner2.txt
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 /root/demo/chown/tree/sub2/inner2.txt
实验三,-L:所有软链全部解引用。link_inner2 指向 ../sub2/inner2.txt(文件而非目录),跟随的结果是 inner2.txt 被直接改到;目录、文件全部变成 u4d2:u4dgrp2,而 link_inner2 软链本身的属主不再被碰(保持 root):
$ chown -R root:root /root/demo/chown/tree /root/demo/chown/link_tree /root/demo/chown/a.txt
$ chown -R -L u4d2:u4dgrp2 /root/demo/chown/link_tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 u4d2 u4dgrp2 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 u4d2 u4dgrp2 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 u4d2 u4dgrp2 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 u4d2 u4dgrp2 7 Oct 15 08:43 inner2.txt
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 root root 13 Oct 15 08:43 /root/demo/chown/a.txt
-P 显式指定时与默认完全一致:
$ chown -R root:root /root/demo/chown/tree /root/demo/chown/link_tree
$ chown -R -P u4d1:u4dgrp1 /root/demo/chown/link_tree
$ ls -l /root/demo/chown/link_tree
lrwxrwxrwx 1 u4d1 u4dgrp1 21 Oct 15 08:43 /root/demo/chown/link_tree -> /root/demo/chown/tree
$ ls -l /root/demo/chown/tree
total 8
drwxr-xr-x 2 root root 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 root root 4096 Oct 15 08:43 sub2
💡 要点:三个实验的差异一目了然——默认/-P 最保守(软链是"墙",改墙不穿墙);-H 只穿透命令行指名的软链,适合"给一个软链路径但想改真实目录"的场景;-L 见链就穿,威力最大也最危险(软链指向上级目录会造成无限循环,GNU 实现靠遍历深度与 inode 记忆规避,但树外文件可能被误改,实测 -L 时 link_inner2 指向的 inner2.txt 被改到即是例证)。运维惯例:拿不准就用默认 -P,需要穿透时优先 -H。
3.12 --from:条件变更,当前属主匹配才修改
--from=当前属主[:当前属组] 给修改加前置条件,只有现状匹配才动手。先把 a.txt 设为 u4d2,然后只把"属主是 u4d2"的文件改为 u4d1;第二条命令条件不匹配(当前属主是 u4d1 不是 root),零输出零改动:
$ chown -c --from=u4d2 u4d1 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d2 to u4d1
$ chown -c --from=root u4d1 /root/demo/chown/a.txt
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 u4d1 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/a.txt
属主与属组可以同时匹配(--from=owner:group),目标端也支持 owner:group:
$ chown -c --from=u4d1:u4dgrp1 u4d2:u4dgrp2 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d1:u4dgrp1 to u4d2:u4dgrp2
$ chown -c --from=u4d2:u4dgrp2 root /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d2 to root
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:43 /root/demo/chown/a.txt
💡 要点:
--from是批量修复时的"安全网"——例如误把整个数据目录 chown 成了 nobody 后,用chown -R --from=nobody webapp:webapp /srv/data只修正真正属于 nobody 的文件,不会误伤目录里本来就有主的其他文件。配合-c使用,改动清单一目了然。
3.13 --reference:参照另一个文件对齐属主属组
--reference=RFILE 不写 owner:group,直接把目标文件的属主与属组改成 RFILE 的现状。先把 ref.txt 设为 u4d2:u4dgrp2 作参照,再把 b.txt 对齐过去:
$ chown u4d2:u4dgrp2 /root/demo/chown/ref.txt
$ ls -l /root/demo/chown/ref.txt
-rw-r--r-- 1 u4d2 u4dgrp2 12 Oct 15 08:43 /root/demo/chown/ref.txt
$ chown u4d1:u4dgrp1 /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d1 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/b.txt
$ chown --reference=/root/demo/chown/ref.txt /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d2 u4dgrp2 13 Oct 15 08:43 /root/demo/chown/b.txt
💡 要点:
--reference一次对齐两个属性,且天然规避名称拼写错误。典型场景:修完 /etc/passwd 或搬家后,"让这批文件的属主跟这个样本文件一模一样";配合find ... -exec chown --reference=样本 {} +可以按目录批量对齐。chgrp 也有同款参数(chgrp 篇实测)。
3.14 错误形态:invalid user / Operation not permitted
第一类错误:用户或组不存在。invalid user / invalid group 在参数解析阶段就失败,即使加 -f 也照样报(3.8 已实测):
$ chown nosuchuser /root/demo/chown/a.txt
chown: invalid user: 'nosuchuser'
$ chown u4d1:nosuchgrp /root/demo/chown/a.txt
chown: invalid group: 'u4d1:nosuchgrp'
第二类错误:普通用户改属主。虽然操作的是 u4d1 自己的文件,内核依然拒绝——改属主是 root(CAP_CHOWN)专属能力。先用 root 把 /tmp/u4d-demo/own.txt 设为 u4d1:u4dgrp1,再以 u4d1 身份尝试让给 u4d2:
$ chown u4d1:u4dgrp1 /tmp/u4d-demo/own.txt
$ ls -ld /tmp/u4d-demo
drwxr-xr-x 2 root root 4096 Oct 15 08:46 /tmp/u4d-demo
$ su u4d1 -c 'chown u4d2 /tmp/u4d-demo/own.txt'
chown: changing ownership of '/tmp/u4d-demo/own.txt': Operation not permitted
$ ls -l /tmp/u4d-demo/own.txt
-rw-r--r-- 1 u4d1 u4dgrp1 5 Oct 15 08:46 /tmp/u4d-demo/own.txt
加 -f 后这条运行期错误被完全静默(属主没变,但输出干净):
$ su u4d1 -c 'chown -f u4d2 /tmp/u4d-demo/own.txt'
$ ls -l /tmp/u4d-demo/own.txt
-rw-r--r-- 1 u4d1 u4dgrp1 5 Oct 15 08:46 /tmp/u4d-demo/own.txt
💡 要点:两条铁律——(1) 改属主只有 root 能做,普通用户即使是对自己 100% 拥有的文件也不行;(2)
-f只吞运行期错误(Operation not permitted),不吞参数错误(invalid user)。另外注意:如果以普通用户去操作一个因父目录权限而不可达的路径,报错会是 cannot access ... Permission denied(本文初测时在 /root 下就撞到过),路径可达性与属主变更失败是两类不同的报错。
3.15 chown 0:0 收回 root
数字形式 0:0 就是 root:root。收回权限是版本库/部署目录清理的最后一步:
$ chown -c 0:0 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from root:u4dgrp2 to 0:0
$ ls -l /root/demo/chown/a.txt
-rw-r--r-- 1 root root 13 Oct 15 08:43 /root/demo/chown/a.txt
💡 要点:注意 -c 输出里"from root:u4dgrp2 to 0:0"——数字与名字混排是常态,输出按输入形式回显。
--no-preserve-root/--preserve-root一对参数控制对/的保护:默认chown -R user /会被拒绝(preserve-root 是现代发行版的默认行为),显式--no-preserve-root才会放行,生产环境永远不要碰后者。
3.16 技术拓展:改属主/属组的三种命令
Linux 上"改文件归属"不止 chown 一条路。主要选项对比——
| 命令 | 能改属主 | 能改属组 | 典型场景 |
|---|---|---|---|
| chown | ✅(root 专属) | ✅ | 一条命令同时改属主+属组,最常用 |
| chgrp | ❌ | ✅ | 只关心属组的批量调整(如移交 www-data 组) |
| usermod -g/-G | ❌ | ❌(改的是用户本身) | 调整用户的主组/附加组,间接影响新建文件的属组 |
💡 技术拓展:usermod 改的是"用户在 /etc/passwd、/etc/group 里的组身份",不动任何既有文件的属组;chown/chgrp 改的是"文件 inode 上的属主属组字段",不动用户身份。两者方向相反、互不替代:想让用户"以后新建文件归新组"用 usermod -g,想把"存量文件归到新组"用 chown/chgrp -R。本文实际采用 chown,因为它一条命令覆盖属主与属组两个维度,配合 -R/--from/--reference 覆盖了绝大多数归属修复场景。
四、实战示例
4.1 把网站目录整体交给运行账户
$ chown -R root:root /root/demo/chown/tree
$ chown -R u4d1:u4dgrp1 /root/demo/chown/tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 u4d1 u4dgrp1 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner2.txt
💡 说明:
-R一次到位;目录、文件、树内软链本身全部换主。生产上把/var/www交给www-data:www-data同理。
4.2 用 --from 只修复误改的文件,不碰本来就有主的
$ chown -c --from=u4d2 u4d1 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d2 to u4d1
💡 说明:目录里混着多批来源不同的文件时,
--from保证只动目标文件,其余原样保留——比先 chown -R root 再逐个改的做法安全得多。
4.3 软链与真实目录分开处理
$ chown -R -H u4d1:u4dgrp1 /root/demo/chown/link_tree
$ ls -lR /root/demo/chown/tree
/root/demo/chown/tree:
total 8
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub1
drwxr-xr-x 2 u4d1 u4dgrp1 4096 Oct 15 08:43 sub2
/root/demo/chown/tree/sub1:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:43 link_inner2 -> ../sub2/inner2.txt
/root/demo/chown/tree/sub2:
total 4
-rw-r--r-- 1 u4d1 u4dgrp1 7 Oct 15 08:43 inner2.txt
💡 说明:入口是软链但想改真实目录时加
-H;若还想把软链自身的属主也一并修好,再补一条不带 -R 的chown -h。
4.4 以参照文件为模板批量对齐
$ chown u4d2:u4dgrp2 /root/demo/chown/ref.txt
$ ls -l /root/demo/chown/ref.txt
-rw-r--r-- 1 u4d2 u4dgrp2 12 Oct 15 08:43 /root/demo/chown/ref.txt
$ chown u4d1:u4dgrp1 /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d1 u4dgrp1 13 Oct 15 08:43 /root/demo/chown/b.txt
$ chown --reference=/root/demo/chown/ref.txt /root/demo/chown/b.txt
$ ls -l /root/demo/chown/b.txt
-rw-r--r-- 1 u4d2 u4dgrp2 13 Oct 15 08:43 /root/demo/chown/b.txt
💡 说明:样本文件 ref.txt 设好一次,目标文件 b.txt 无论当前归属是什么(此处从 u4d1:u4dgrp1 一键对齐),一条
--reference照抄成 u4d2:u4dgrp2,避免属主属组名打错。
4.5 一条命令完成"移交 + 收回"
$ chown -c u4d2:u4dgrp1 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d1:root to u4d2:u4dgrp1
$ chown -c 0:0 /root/demo/chown/a.txt
changed ownership of '/root/demo/chown/a.txt' from u4d2:u4dgrp1 to 0:0
💡 说明:交接前先把文件给接手方(owner:group 一步到位),验收完毕收回 root。-c 的输出可直接粘进交接记录,from/to 两端把来龙去脉记得清清楚楚。
五、注意事项与常见问题
Q1:chown 改了属主,属组为什么也跟着变了?
大概率用了 owner: 或 owner.group 形式(冒号/点号结尾),属组会被重置为属主的登录组;或者目标端写成了 owner:group。只想改属主就只写属主名,别带任何分隔符(见 3.2 实测)。
Q2:普通用户执行 chown 报 Operation not permitted,文件明明是自己的?
改属主是内核 CAP_CHOWN 能力,只有 root(或具备该能力的进程)可以。普通用户能做的只有把属组改成自己所属的组之一(见 chgrp 篇实测)。需要"转赠"文件时让 root 执行,或用 install -o user -g group 重建文件。
Q3:chown -R 之后进入目录报 Permission denied?
属主变了但目录模式位可能没跟上(比如原来 700 且新属主无 x 权限)。chown 只改归属不改权限位,两者要配套:chown -R u4d1:u4dgrp1 dir && chmod -R u+rx dir。目录的 x 权限含义见 chmod 篇。
Q4:-R 会顺着软链把链接目标的文件也改了吗?
默认不会(-P 语义,只改软链本身,见 3.11 实验一)。显式 -R -L 才会全部穿透,且软链指到树外时树外文件也会被改到,慎用;"入口是软链"的场景用 -R -H。
Q5:为什么 chown -f 还报 invalid user? -f 抑制的是运行期错误(Operation not permitted 一类);用户名解析发生在参数处理阶段,属于程序自身的合法性检查,不受 -f 影响(见 3.8、3.14 两处实测)。
Q6:想把属主设成一个名字是纯数字的用户怎么办?
chown 123 FILE 会被当成 uid=123。coreutils 的约定是给用户名补冒号消除歧义:chown '123:' FILE 表示名为 123 的用户(组取其登录组)。
Q7:批量改了几千个文件,怎么确认到底改了哪些?
用 -c(只打印变化行)或 -v(打印全部处理行),输出形如 changed ownership of '...' from A to B,可直接落日志审计(见 3.6、3.7 实测)。
六、参考链接
- GNU coreutils 官方手册(chown invocation):https://www.gnu.org/software/coreutils/manual/html_node/chown-invocation.html
- coreutils 在线手册页:https://www.gnu.org/software/coreutils/chown
- man7.org Linux man-pages chown(1):https://man7.org/linux/man-pages/man1/chown.1.html
- man7.org Linux man-pages chown(2)(系统调用与 CAP_CHOWN):https://man7.org/linux/man-pages/man2/chown.2.html
- 本地手册:
man 1 chown、info '(coreutils) chown invocation'