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

GNU chgrp 参数实测:基本改组与数字 GID、-c/-v/-f、-h 软链解引用、-R 与 -H/-L/-P 遍历语义、--reference,以及普通用户改组的属主+组成员双条件权限边界,Ubuntu 22.04 实测。

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

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 才是稳妥写法。

六、参考链接