chgrp 命令详解与参数实测(Ubuntu 22.04)
GNU chgrp 参数实测:基本改组与数字 GID、-c/-v/-f、-h 软链解引用、-R 与 -H/-L/-P 遍历语义、--reference,以及普通用户改组的属主+组成员双条件权限边界,Ubuntu 22.04 实测。
chgrp 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 chgrp 的每一个常用参数——基本改组、数字 GID、
-c/-v/-f输出控制、-h软链本身与默认解引用、-R递归及-H/-L/-P三种遍历语义、--reference参照文件,以及"普通用户只能把文件改成自己所属的组"这条权限铁律的完整实测边界,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
本篇所有命令与回显均来自同一台 Ubuntu 22.04 服务器的实测,演示数据统一放在 /root/demo/chgrp/ 下,另在 /tmp/u4d-demo/ 做普通用户权限实验。读者在任意 Ubuntu 22.04 机器上按相同步骤均可复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- chgrp 版本:GNU coreutils 8.32(
chgrp --version实测) - chgrp 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/chgrp/(root 身份操作);/tmp/u4d-demo/(普通用户实验) - 测试用户与组:
u4d1(uid=1000,主组 u4d1;实验中途用 usermod 加入 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 语法与测试数据
chgrp 的语法共两种形态(摘自帮助 Usage 行):chgrp [OPTION]... GROUP FILE... 把文件的属组改为 GROUP,chgrp [OPTION]... --reference=RFILE FILE... 参照另一个文件对齐属组。它只管属组一件事——不能改属主,这是它与 chown 的本质分工。
准备演示数据——三个普通文件、一棵带子目录与树内软链的目录树、两个软链:
mkdir -p /root/demo/chgrp/tree/sub1 /root/demo/chgrp/tree/sub2
echo 'chgrp demo a' > /root/demo/chgrp/a.txt
echo 'chgrp demo b' > /root/demo/chgrp/b.txt
echo 'ref content' > /root/demo/chgrp/ref.txt
echo inner1 > /root/demo/chgrp/tree/sub1/inner1.txt
echo inner2 > /root/demo/chgrp/tree/sub2/inner2.txt
ln -s ../sub2/inner2.txt /root/demo/chgrp/tree/sub1/link_inner2
ln -s /root/demo/chgrp/a.txt /root/demo/chgrp/link_a
ln -s /root/demo/chgrp/tree /root/demo/chgrp/link_tree
验证数据就绪:
$ ls -l /root/demo/chgrp
total 16
-rw-r--r-- 1 root root 13 Oct 15 08:53 a.txt
-rw-r--r-- 1 root root 13 Oct 15 08:53 b.txt
lrwxrwxrwx 1 root root 22 Oct 15 08:53 link_a -> /root/demo/chgrp/a.txt
lrwxrwxrwx 1 root root 21 Oct 15 08:53 link_tree -> /root/demo/chgrp/tree
-rw-r--r-- 1 root root 12 Oct 15 08:53 ref.txt
drwxr-xr-x 4 root root 4096 Oct 15 08:53 tree
确认测试用户与组的身份,后文权限边界实验围绕它们展开:
$ 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)
$ getent group u4dgrp1
u4dgrp1:x:1001:u4d2
$ getent group u4dgrp2
u4dgrp2:x:1002:u4d2
2.2 --help 全量输出
chgrp --help 输出全部参数清单(实测原样保留,可作为参数速查):
Usage: chgrp [OPTION]... GROUP FILE...
or: chgrp [OPTION]... --reference=RFILE FILE...
Change the group of each FILE to GROUP.
With --reference, change the group 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
--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)
--no-preserve-root do not treat '/' specially (the default)
--preserve-root fail to operate recursively on '/'
--reference=RFILE use RFILE's group rather than specifying a
GROUP value
-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
Examples:
chgrp staff /u Change the group of /u to "staff".
chgrp -hR staff /u Change the group of /u and subfiles to "staff".
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/chgrp>
or available locally via: info '(coreutils) chgrp invocation'
💡 说明:参数清单与 chown 几乎一一对应,唯一少了
--from(chgrp 没有"按当前属组条件变更"的能力,属组修复要靠--reference或 find 过滤)。-H/-L/-P同样只在配合-R时生效,多个同时给出时只有最后一个生效。
2.3 版本确认
$ chgrp --version | head -1
chgrp (GNU coreutils) 8.32
三、参数逐项实测
3.1 基本用法:修改文件属组
命令格式为 chgrp GROUP FILE。属主保持 root 不动,只有第四列(属组)变化:
$ chgrp u4dgrp1 /root/demo/chgrp/a.txt
$ ls -l /root/demo/chgrp/a.txt
-rw-r--r-- 1 root u4dgrp1 13 Oct 15 08:53 /root/demo/chgrp/a.txt
💡 要点:chgrp 不接受 owner:group 之类的写法,参数位置只有一个 GROUP。改属组比改属主宽松:文件属主可以把属组改成自己所属的任意组(主组或附加组),root 可以改成任意组(3.10 有完整边界实测)。
3.2 数字 GID
GROUP 可以直接写数字,绕过组名解析——系统里没有这个组名也照改不误。用 ls -ln(数字显示)与 ls -l(名称显示)对照验证:
$ chgrp 1002 /root/demo/chgrp/a.txt
$ ls -ln /root/demo/chgrp/a.txt
-rw-r--r-- 1 0 1002 13 Oct 15 08:53 /root/demo/chgrp/a.txt
$ ls -l /root/demo/chgrp/a.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:53 /root/demo/chgrp/a.txt
$ chgrp 54321 /root/demo/chgrp/b.txt
$ ls -ln /root/demo/chgrp/b.txt
-rw-r--r-- 1 0 54321 13 Oct 15 08:53 /root/demo/chgrp/b.txt
💡 要点:gid=54321 在 /etc/group 里根本没有对应条目,但 chgrp 照样成功——inode 上存的本来就是数字。注意
ls -l显示0 54321时第三列会因为查不到用户名而直接显示数字。这个特性适合两台机器 GID 语义不一致时的批量修正,但副作用是"孤儿组":后续 groupadd 占用了 54321,这批文件就意外归了新组。
3.3 -c/--changes:只输出发生变化的条目
a.txt 当前属组是 u4dgrp2,改为 u4dgrp1 时输出 changed;重复执行无变化,零输出:
$ chgrp -c u4dgrp1 /root/demo/chgrp/a.txt
changed group of '/root/demo/chgrp/a.txt' from u4dgrp2 to u4dgrp1
$ chgrp -c u4dgrp1 /root/demo/chgrp/a.txt
💡 要点:注意输出措辞与 chown 的差别——chown 是 "changed ownership of",chgrp 是 "changed group of"。批量脚本里用
-c只留真正动过的条目,输出即审计记录。
3.4 -v/--verbose:无论是否变化都输出
-v 对每个处理的文件都打印诊断行,变化输出 changed,无变化输出 retained:
$ chgrp -v u4dgrp2 /root/demo/chgrp/a.txt
changed group of '/root/demo/chgrp/a.txt' from u4dgrp1 to u4dgrp2
$ chgrp -v u4dgrp2 /root/demo/chgrp/a.txt
group of '/root/demo/chgrp/a.txt' retained as u4dgrp2
💡 要点:retained 行说明命令确实处理到了该文件、只是属性已一致。排查"遍历了哪些路径、有没有漏"时
-v比-c更合适。
3.5 -f/--silent:抑制运行期错误消息
-f 抑制大多数错误消息。参数解析错误(invalid group)不受抑制,照报:
$ chgrp -f nosuchgrp /root/demo/chgrp/a.txt
chgrp: invalid group: 'nosuchgrp'
运行期错误则被完全静默——u4d1 无权改 a.txt(属主是 root)的属组,加 -f 后没有输出,属组也没变:
$ su u4d1 -c 'chgrp -f u4dgrp2 /root/demo/chgrp/a.txt'
$ ls -l /root/demo/chgrp/a.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:53 /root/demo/chgrp/a.txt
💡 要点:与 chown 的
-f行为完全一致:只吞运行期失败(Operation not permitted 一类),不吞参数错误(invalid group)。静默失败要靠退出码兜底检查。
3.6 -h/--no-dereference:改软链本身;默认解引用改目标
link_a 是指向 a.txt 的软链。默认(--dereference)改的是 a.txt,软链本身不动:
$ chgrp u4dgrp2 /root/demo/chgrp/link_a
$ ls -l /root/demo/chgrp/link_a /root/demo/chgrp/a.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:53 /root/demo/chgrp/a.txt
lrwxrwxrwx 1 root root 22 Oct 15 08:53 /root/demo/chgrp/link_a -> /root/demo/chgrp/a.txt
加 -h 后作用于软链本身,a.txt 没动:
$ chgrp -h u4dgrp1 /root/demo/chgrp/link_a
$ ls -l /root/demo/chgrp/link_a /root/demo/chgrp/a.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:53 /root/demo/chgrp/a.txt
lrwxrwxrwx 1 root u4dgrp1 22 Oct 15 08:53 /root/demo/chgrp/link_a -> /root/demo/chgrp/a.txt
💡 要点:软链属主/属组虽然不影响访问目标文件,但 rsync -a、tar 等归档工具会原样保留它,属组错乱会让恢复出来的链接与预期不符。帮助文本注明
-h只在"系统能改软链属主"的内核上有意义,Linux 从 2.1.126 起支持。
3.7 -R/--recursive:递归修改整棵目录树
-R 把目录、子目录、目录内所有文件(含树内软链本身)的属组一并修改。先复位再统一交给 u4dgrp1:
$ chgrp -R root /root/demo/chgrp/tree
$ chgrp -R u4dgrp1 /root/demo/chgrp/tree
$ ls -lR /root/demo/chgrp/tree
/root/demo/chgrp/tree:
total 8
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub1
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub2
/root/demo/chgrp/tree/sub1:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner1.txt
lrwxrwxrwx 1 root u4dgrp1 18 Oct 15 08:53 link_inner2 -> ../sub2/inner2.txt
/root/demo/chgrp/tree/sub2:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner2.txt
💡 要点:link_inner2 这条树内软链的属组也变成了 u4dgrp1,说明
-R遍历对"不跟随"的软链是直接改软链本身。chgrp -R 组 目录是把项目目录交给协作组的标准做法(如/srv/project交给 devteam 组)。
3.8 -R 默认遍历语义(等价 -P:不跟随软链)
link_tree 是指向 tree 的命令行软链。默认 -R(等价 -P)只改了 link_tree 软链本身的属组,tree 内部纹丝不动:
$ chgrp -R root /root/demo/chgrp/tree /root/demo/chgrp/link_tree
$ chgrp -R u4dgrp2 /root/demo/chgrp/link_tree
$ ls -l /root/demo/chgrp/link_tree
lrwxrwxrwx 1 root u4dgrp2 21 Oct 15 08:53 /root/demo/chgrp/link_tree -> /root/demo/chgrp/tree
$ ls -lR /root/demo/chgrp/tree
/root/demo/chgrp/tree:
total 8
drwxr-xr-x 2 root root 4096 Oct 15 08:53 sub1
drwxr-xr-x 2 root root 4096 Oct 15 08:53 sub2
/root/demo/chgrp/tree/sub1:
total 4
-rw-r--r-- 1 root root 7 Oct 15 08:53 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:53 link_inner2 -> ../sub2/inner2.txt
/root/demo/chgrp/tree/sub2:
total 4
-rw-r--r-- 1 root root 7 Oct 15 08:53 inner2.txt
💡 要点:这是最容易踩的坑——"明明 -R 了,怎么里面的文件没变?"原因就是入口参数是个软链,-P 语义把它当独立对象处理。解决办法见 3.9 的 -H。
3.9 -R -H:命令行软链解引用,树内软链不跟随
-H 只对命令行参数里的软链解引用。link_tree 被穿透,整棵 tree 被改到;树内的 link_inner2 不被跟随,保持 root(它指向的 inner2.txt 是靠目录遍历改到的,不是靠穿透软链):
$ chgrp -R root /root/demo/chgrp/tree /root/demo/chgrp/link_tree
$ chgrp -R -H u4dgrp1 /root/demo/chgrp/link_tree
$ ls -lR /root/demo/chgrp/tree
/root/demo/chgrp/tree:
total 8
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub1
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub2
/root/demo/chgrp/tree/sub1:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:53 link_inner2 -> ../sub2/inner2.txt
/root/demo/chgrp/tree/sub2:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner2.txt
$ ls -l /root/demo/chgrp/tree/sub1/link_inner2
lrwxrwxrwx 1 root root 18 Oct 15 08:53 /root/demo/chgrp/tree/sub1/link_inner2 -> ../sub2/inner2.txt
-R -L 则是所有软链全部解引用:link_inner2 指向的 inner2.txt 被直接改到(u4dgrp2),全部目录文件换组,而 link_inner2 软链本身保持 root:
$ chgrp -R root /root/demo/chgrp/tree /root/demo/chgrp/link_tree
$ chgrp -R -L u4dgrp2 /root/demo/chgrp/link_tree
$ ls -lR /root/demo/chgrp/tree
/root/demo/chgrp/tree:
total 8
drwxr-xr-x 2 root u4dgrp2 4096 Oct 15 08:53 sub1
drwxr-xr-x 2 root u4dgrp2 4096 Oct 15 08:53 sub2
/root/demo/chgrp/tree/sub1:
total 4
-rw-r--r-- 1 root u4dgrp2 7 Oct 15 08:53 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:53 link_inner2 -> ../sub2/inner2.txt
/root/demo/chgrp/tree/sub2:
total 4
-rw-r--r-- 1 root u4dgrp2 7 Oct 15 08:53 inner2.txt
-P 显式指定时与默认一致(只改软链本身,不进树):
| 选项 | 遍历语义 | 对命令行参数中的软链 | 对遍历途中遇到的软链 |
|---|---|---|---|
| -P(默认) | 不跟随任何软链 | 只改软链本身 | 只改软链本身 |
| -H | 仅命令行参数解引用 | 跟随并进入其指向的目录树 | 不跟随,只改软链本身 |
| -L | 全部解引用 | 跟随 | 跟随,指向目录就下钻 |
💡 要点:三个语义选项与 chown 完全同款,帮助文本同样声明"多个同时给出时只有最后一个生效"。运维惯例:默认 -P 最保守;"入口是软链"用 -H;-L 见链就穿,软链指到树外时树外文件也会被改到,慎用。
3.10 root 任意改 vs 普通用户只能改成自己所属的组
这是 chgrp 最核心的权限边界,本文用三步递进实验完整实测。
第一步,root 把 a.txt 改回自己的组,验证 root 无限制:
$ chgrp root /root/demo/chgrp/a.txt
$ ls -l /root/demo/chgrp/a.txt
-rw-r--r-- 1 root root 13 Oct 15 08:53 /root/demo/chgrp/a.txt
第二步,普通用户改一个"自己既不是属主、目标组也不属于自己"的文件。/tmp/u4d-demo/grp.txt 属主 root、属组 u4dgrp2,u4d1 两头都不沾,报 Operation not permitted:
$ mkdir -p /tmp/u4d-demo
$ echo demo > /tmp/u4d-demo/grp.txt
$ chgrp u4dgrp2 /tmp/u4d-demo/grp.txt
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 root u4dgrp2 5 Oct 15 08:53 /tmp/u4d-demo/grp.txt
$ su u4d1 -c 'chgrp u4dgrp1 /tmp/u4d-demo/grp.txt'
chgrp: changing group of '/tmp/u4d-demo/grp.txt': Operation not permitted
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 root u4dgrp2 5 Oct 15 08:53 /tmp/u4d-demo/grp.txt
第三步,把 u4d1 加入 u4dgrp1 后重试——仍然失败!因为 grp.txt 的属主是 root,u4d1 不是属主:
$ usermod -aG u4dgrp1 u4d1
$ id u4d1
uid=1000(u4d1) gid=1000(u4d1) groups=1000(u4d1),1001(u4dgrp1)
$ su u4d1 -c 'chgrp u4dgrp1 /tmp/u4d-demo/grp.txt'
chgrp: changing group of '/tmp/u4d-demo/grp.txt': Operation not permitted
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 root u4dgrp2 5 Oct 15 08:53 /tmp/u4d-demo/grp.txt
$ su u4d1 -c 'chgrp u4d2 /tmp/u4d-demo/grp.txt'
chgrp: changing group of '/tmp/u4d-demo/grp.txt': Operation not permitted
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 root u4dgrp2 5 Oct 15 08:53 /tmp/u4d-demo/grp.txt
第四步,把 grp.txt 的属主改为 u4d1,双条件(属主 + 目标组是自己所属组)再验证:改成 u4dgrp1(u4d1 的附加组)成功,改成 u4d2(u4d1 不在的组)失败:
$ mkdir -p /tmp/u4d-demo
$ echo demo > /tmp/u4d-demo/grp.txt
$ chown u4d1:u4d2 /tmp/u4d-demo/grp.txt
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 u4d1 u4d2 5 Oct 15 08:55 /tmp/u4d-demo/grp.txt
$ su u4d1 -c 'chgrp u4dgrp1 /tmp/u4d-demo/grp.txt'
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 u4d1 u4dgrp1 5 Oct 15 08:55 /tmp/u4d-demo/grp.txt
$ su u4d1 -c 'chgrp u4d2 /tmp/u4d-demo/grp.txt'
chgrp: changing group of '/tmp/u4d-demo/grp.txt': Operation not permitted
$ ls -l /tmp/u4d-demo/grp.txt
-rw-r--r-- 1 u4d1 u4dgrp1 5 Oct 15 08:55 /tmp/u4d-demo/grp.txt
💡 要点:普通用户 chgrp 成功需要同时满足两个条件——(1) 是文件的属主;(2) 目标组是自己所属的组(主组或附加组,chgrp 会自动带上辅助组身份)。缺一个都是 Operation not permitted。这正对应 chown(2) 手册的规则:进程的有效 UID 必须等于文件属主,且 GROUP 必须是进程有效 GID 或辅助组之一。
3.11 --reference:参照另一个文件对齐属组
--reference=RFILE 把目标文件的属组改成 RFILE 的属组。先把 ref.txt 设为 u4dgrp2 作参照,再把 b.txt(原 u4dgrp1)对齐过去:
$ chgrp u4dgrp2 /root/demo/chgrp/ref.txt
$ ls -l /root/demo/chgrp/ref.txt
-rw-r--r-- 1 root u4dgrp2 12 Oct 15 08:53 /root/demo/chgrp/ref.txt
$ chgrp u4dgrp1 /root/demo/chgrp/b.txt
$ chgrp --reference=/root/demo/chgrp/ref.txt /root/demo/chgrp/b.txt
$ ls -l /root/demo/chgrp/b.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:53 /root/demo/chgrp/b.txt
💡 要点:
--reference一次只对齐属组(chown 的同款参数对齐属主+属组两个属性)。适合"让这批文件跟样本文件同组"的批量场景,且天然规避组名拼写错误。
3.12 错误形态:不存在的组
组名不存在时在参数解析阶段直接失败:
$ chgrp nosuchgrp /root/demo/chgrp/a.txt
chgrp: invalid group: 'nosuchgrp'
💡 要点:与 chown 一致,invalid group 不受
-f抑制(3.5 已实测)。组名可以含点号吗?可以,所以 chgrp 不存在 chown 那种点号歧义问题——这也是"只改组时用 chgrp 比 chown :group 更不容易踩坑"的一个小理由。
3.13 技术拓展:改属组的四种命令
Linux 上"改组"这个动作,命令不止一个,改的对象也不同。主要选项对比——
| 命令 | 改的对象 | 能力边界 | 典型场景 |
|---|---|---|---|
| chgrp | 文件 inode 的属组字段 | 属主可把文件改到自己所属的组;root 任意改 | 批量移交文件给协作组 |
| chown :group | 文件 inode 的属组字段 | 与 chgrp 等价,还能顺带改属主 | 同时改属主和属组时顺路 |
| usermod -g/-G | 用户在 /etc/group 里的身份 | 不动任何既有文件 | 调整用户主组/附加组,影响以后新建文件 |
| newgrp | 当前 shell 会话的有效 GID | 只影响会话内新建文件,需组密码或已是成员 | 临时以某组身份工作,不永久改身份 |
💡 技术拓展:chgrp/chown 改的是"存量文件",usermod 改的是"用户身份(影响增量文件)",newgrp 改的是"会话临时身份"。本文实际采用 chgrp,因为它是语义最直白的"只改组"工具——没有 owner 前缀、没有点号歧义、普通用户权限边界清晰,配合 -R/--reference 覆盖绝大多数属组移交场景。
四、实战示例
4.1 把项目目录批量移交给协作组
$ chgrp -R u4dgrp1 /root/demo/chgrp/tree
$ ls -lR /root/demo/chgrp/tree
/root/demo/chgrp/tree:
total 8
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub1
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub2
/root/demo/chgrp/tree/sub1:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner1.txt
lrwxrwxrwx 1 root u4dgrp1 18 Oct 15 08:53 link_inner2 -> ../sub2/inner2.txt
/root/demo/chgrp/tree/sub2:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner2.txt
💡 说明:属主 root 保持不变,属组整体换成 u4dgrp1。生产上等价于
chgrp -R devteam /srv/project,组员需要目录写权限时再配chmod -R g+w(见 chmod 篇)。
4.2 入口是软链时的正确姿势
$ chgrp -R root /root/demo/chgrp/tree /root/demo/chgrp/link_tree
$ chgrp -R -H u4dgrp1 /root/demo/chgrp/link_tree
$ ls -lR /root/demo/chgrp/tree
/root/demo/chgrp/tree:
total 8
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub1
drwxr-xr-x 2 root u4dgrp1 4096 Oct 15 08:53 sub2
/root/demo/chgrp/tree/sub1:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner1.txt
lrwxrwxrwx 1 root root 18 Oct 15 08:53 link_inner2 -> ../sub2/inner2.txt
/root/demo/chgrp/tree/sub2:
total 4
-rw-r--r-- 1 root u4dgrp1 7 Oct 15 08:53 inner2.txt
💡 说明:拿到手的是软链路径(如
/opt/app -> /srv/app-v2)时,-H让遍历穿透入口直达真实目录,树内软链不受影响,改完不会误伤链接指向的树外文件。
4.3 以参照文件为模板对齐一批文件
$ chgrp u4dgrp2 /root/demo/chgrp/ref.txt
$ chgrp --reference=/root/demo/chgrp/ref.txt /root/demo/chgrp/b.txt
$ ls -l /root/demo/chgrp/b.txt
-rw-r--r-- 1 root u4dgrp2 13 Oct 15 08:53 /root/demo/chgrp/b.txt
💡 说明:ref.txt 定好基准组,b.txt 一条命令对齐。配合 find 可以整目录对齐:
find /data -exec chgrp --reference=/data/样本 {} +。
4.4 数字 GID 修正跨机迁移的"孤儿组"
$ chgrp 54321 /root/demo/chgrp/b.txt
$ ls -ln /root/demo/chgrp/b.txt
-rw-r--r-- 1 0 54321 13 Oct 15 08:53 /root/demo/chgrp/b.txt
💡 说明:从旧机器搬来的数据带着本机不存在的 GID 时,可以先用
find /data -nogroup找出这批文件,再统一改成正确的组名;数字形式也常用于"先按数字占位、组稍后创建"的初始化脚本。
4.5 变更审计:-c 留痕
$ chgrp -c u4dgrp1 /root/demo/chgrp/a.txt
changed group of '/root/demo/chgrp/a.txt' from u4dgrp2 to u4dgrp1
$ chgrp -c u4dgrp1 /root/demo/chgrp/a.txt
💡 说明:第一行 changed 是变更记录,第二次执行零输出说明已到位。把
-c输出重定向到变更日志,就是最朴素的属组审计。
五、注意事项与常见问题
Q1:普通用户 chgrp 报 Operation not permitted,可我明明在那个组里?
两个条件缺一不可:必须是文件的属主,且目标组是自己所属的组。3.10 第三步实测过:u4d1 已在 u4dgrp1 里,但文件属主是 root,照样失败。先确认文件是不是自己的(ls -l 第三列)。
Q2:usermod 把用户加进组之后,chgrp 立刻生效吗?
对新起的 su/ssh 会话立即生效(su 会按当前 /etc/group 重建组身份);但用户已存在的登录会话要重新登录才能带上新组。3.10 的实测全部走 su u4d1 -c,每次都是新会话,所以 usermod 后立即有效。
Q3:chgrp -R 后"里面的文件没变"?
入口参数是个软链。默认 -P 语义把软链当独立对象,只改软链本身;加 -H 穿透命令行软链即可(3.8、3.9 对照实测)。
Q4:chgrp 能改成任意组吗? 普通用户只能改成自己所属的组;root 无限制。要给普通用户"任意改"的能力,只能 root 代劳或用 sudo 白名单授权,内核没有"授权某人 chgrp 任意组"的开关。
Q5:chgrp 和 chown :group 到底用哪个?
功能上完全等价(chown 的 :group 形式就是顺手支持)。chgrp 语义更单一、参数更简单,脚本里只改组时优先 chgrp;需要"属主属组一起改"才用 chown。
Q6:改成的组名不存在会怎样?
参数解析阶段直接报 chgrp: invalid group: '...' 并退出,文件不动(3.12 实测)。想让文件挂一个"还不存在的组"只能用数字 GID(3.2 实测,系统里查不到组名也照改)。
Q7:-f 把错误都吞了,怎么知道有没有失败?
-f 只抑制运行期报错的输出,命令退出码仍然非零。脚本里 chgrp -R -f g dir || echo failed 才是稳妥写法。
六、参考链接
- GNU coreutils 官方手册(chgrp invocation):https://www.gnu.org/software/coreutils/manual/html_node/chgrp-invocation.html
- coreutils 在线手册页:https://www.gnu.org/software/coreutils/chgrp
- man7.org Linux man-pages chgrp(1):https://man7.org/linux/man-pages/man1/chgrp.1.html
- man7.org Linux man-pages chown(2)(chgrp 的系统调用与权限规则):https://man7.org/linux/man-pages/man2/chown.2.html
- 本地手册:
man 1 chgrp、info '(coreutils) chgrp invocation'