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

id 命令逐参数实测详解:默认输出格式、用户名与数字 UID 双操作数、-u/-g/-G 三类 ID 提取、-n 名称组合(id -un/id -Gn)、-r 真实与有效 UID 的 setpriv 分离实测、-Z 在非 SELinux 内核的真实报错、-a 兼容项与 -z NUL 分隔,含 root 权限守门与组成员检查实战,全部回显在 Ubuntu 22.04 实测通过。

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

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

目标:全面掌握 id 的每一个常用参数——默认输出格式、指定用户操作数(用户名/数字 UID)、-u/-g/-G 三类 ID 单独提取、-n 名称模式及其组合(id -un/id -Gn)、-r 真实 ID 与有效 ID 的差异、-Z SELinux 上下文在非 SELinux 内核下的真实报错、-a 兼容选项与 -z NUL 分隔符,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。

一、环境信息

本节记录实测环境与演示数据,便于读者对照复现。id 是 GNU coreutils 中负责「身份查询」的核心工具:一条命令同时回答当前(或指定)用户的 uid、gid 与全部附属组。它比 whoami 信息量更大,也是脚本里做权限判断的首选(用数字 UID 判断,不受用户名改名影响)。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • id 版本:GNU coreutils 8.32(软件包 coreutils 8.32-4.1ubuntu1.4,见 2.3 实测)
  • setpriv 版本:util-linux 2.37.2(用于 3.5 节构造「真实/有效 UID 分离」场景)
  • Shell:GNU bash 5.1.16
  • 演示目录:/root/demo/id/
  • 测试用户:u4a-demo(普通用户,uid=1004,主组 gid=1006,附加组 u4a-grp=1007)
  • 测试用户组:u4a-grp(gid=1007,成员 u4a-demo),用于展示附属组相关参数

二、命令概览

2.1 语法与测试数据

id 的语法为:

id [OPTION]... [USER]...

不带 USER 时查询当前进程的身份;带 USER 时查询指定用户的身份——USER 既可以是用户名,也可以是数字 UID(实测见 3.2)。选项分三类:取哪类 ID(-u/-g/-G/-Z)、以什么形式输出(-n 名称、-z 分隔符)、以哪个口径(-r 真实而非有效)。

为了实测附属组(-G/-Gn)的价值,先准备一个带附加组的测试用户:创建组 u4a-grp 并把 u4a-demo 加入其中,再建演示目录:

groupadd -f u4a-grp
usermod -aG u4a-grp u4a-demo
mkdir -p /root/demo/id

验证数据就绪——id u4a-demo 的 groups 段应同时列出主组与附加组,getent group 应能看到成员记录:

$ id u4a-demo
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)
$ getent group u4a-grp
u4a-grp:x:1007:u4a-demo

两条回显相互印证:u4a-demo 的主组是 u4a-demo(1006),附加组是 u4a-grp(1007)。后续 -G/-Gn 的实测都基于这组数据。

2.2 --help 全量回显

id 的选项比 whoami 丰富得多。以下为 id --help 完整回显,逐行照抄:

$ id --help
Usage: id [OPTION]... [USER]...
Print user and group information for each specified USER,
or (when USER omitted) for the current user.

  -a             ignore, for compatibility with other versions
  -Z, --context  print only the security context of the process
  -g, --group    print only the effective group ID
  -G, --groups   print all group IDs
  -n, --name     print a name instead of a number, for -ugG
  -r, --real     print the real ID instead of the effective ID, with -ugG
  -u, --user     print only the effective user ID
  -z, --zero     delimit entries with NUL characters, not whitespace;
                   not permitted in default format
      --help     display this help and exit
      --version  output version information and exit

Without any OPTION, print some useful set of identified information.

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/id>
or available locally via: info '(coreutils) id invocation'

💡 说明:帮助里的两处细节值得划线——① -n 标注「for -ugG」,即名称模式只对 -u/-g/-G 生效;② -r 标注「with -ugG」,即真实模式也只对这三个选项生效;③ -z 标注「not permitted in default format」,默认格式下用 -z 会直接报错(3.8 实测复现)。-a 的说明是 ignore, for compatibility——纯兼容占位,加了等于没加(3.7 实测)。

2.3 版本信息

用 --version 确认版本与作者信息:

$ id --version
id (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Arnold Robbins and David MacKenzie.

再用 type 确认它是独立程序而非 shell 内建:

$ type id
id is hashed (/usr/bin/id)

is hashed (/usr/bin/id) 表明 bash 缓存了 /usr/bin/id 的路径,id 是 coreutils 的独立可执行文件。只需版本号一行时取首行:

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

三、参数逐项实测

3.1 无参数:默认格式输出完整身份

不带任何选项与操作数,id 以固定格式打印 uid、gid 与 groups 三段:

$ id
uid=0(root) gid=0(root) groups=0(root)

当前进程以 root 身份运行,三段都是 0(root)。确认退出码:

$ id; echo "exit=$?"
uid=0(root) gid=0(root) groups=0(root)
exit=0

💡 要点:默认格式每段都是 数字(名称) 成对出现,段与段之间空格分隔。root 的 groups 段只有一个组;普通用户通常至少有「主组+附加组」两个成员(见 3.2 的 u4a-demo)。权限判断只看 uid=0 即可认定 root。

3.2 指定用户操作数:查任何用户的身份(用户名或数字 UID)

id 后跟用户名,即查询该用户(不限于当前进程):

$ id root
uid=0(root) gid=0(root) groups=0(root)
$ id u4a-demo
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)

操作数也可以直接给数字 UID,id 会反查对应的用户名——这是「从数字找名字」的实用技巧:

$ id 1004
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)
$ id 0
uid=0(root) gid=0(root) groups=0(root)

查询不存在的用户则报错并以状态码 1 退出:

$ id nosuchuser123; echo "exit=$?"
id: 'nosuchuser123': no such user
exit=1

注意 id 只认用户、不认组——传组名会得到 no such user:

$ id u4a-grp; echo "exit=$?"
id: 'u4a-grp': no such user
exit=1

💡 要点:id <数字> 是 uid 与用户名互查的最短路径(反向用 id -un <数字> 更清爽,见 3.4)。查组要用 getent group <组名> 或 id -gn <用户>。报错格式统一为 id: '<操作数>': no such user,退出码 1,脚本可用作存在性判断。

3.3 -u / -g / -G:单独提取用户 ID、组 ID、全部组 ID

-u(--user)只输出有效用户 ID 的数字:

$ id -u
0
$ id -u u4a-demo
1004
$ id -u root
0

-g(--group)只输出有效组 ID 的数字:

$ id -g
0
$ id -g u4a-demo
1006

-G(--groups)输出全部组 ID(主组 + 所有附加组),空格分隔:

$ id -G
0
$ id -G u4a-demo
1006 1007

root 只有主组 0,所以 -G 输出单值;u4a-demo 因附加了 u4a-grp,输出两个值。三者对比一目了然:

命令输出内容root 实测u4a-demo 实测
id -u有效用户 ID01004
id -g有效组 ID(主组)01006
id -G全部组 ID(主组+附加组)01006 1007

💡 要点:-u 是脚本里判 root 的标准写法来源([ "$(id -u)" -eq 0 ]);-G 的数字列表是文件权限核算的依据——进程对某文件的访问权 = 属主/属组/其他 三级匹配,其中属组匹配范围就来自 -G 列出的全部组。

3.4 -n 名称模式与组合:id -un / id -gn / id -Gn

-n(--name)把 -u/-g/-G 的数字输出换成名称。帮助原文注明 -n 只对 -ugG 生效。最常用的三个组合:

$ id -un
root
$ id -gn
root
$ id -Gn
root

对普通用户查询,-Gn 一次给出主组与附加组名:

$ id -Gn u4a-demo
u4a-demo u4a-grp

-n 与选项的先后顺序无关(coreutils 选项可任意排列),拆开写与连写等价:

$ id -u -n
root
$ id -G -n u4a-demo
u4a-demo u4a-grp

id -un 还支持数字 UID 反查用户名,一行完成「数字→名字」转换:

$ id -un 1004
u4a-demo

💡 要点:id -un 与 whoami 输出完全一致(whoami 的帮助原文即声明 Same as id -un.);id -Gn 等价于 groups <用户> 命令;id -gn <数字> 可做 gid→组名转换。三连组合 -un/-gn/-Gn 是日常使用频率最高的形态。

3.5 -r 真实模式:有效 ID 与真实 ID 的分水岭

-r(--real)让 -u/-g/-G 输出真实 ID 而非有效 ID。先在普通(未分离)场景下看——二者相同,-r 无可见差异:

$ id -ur
0
$ id -gr
0
$ id -r -u
0
$ id -r -u u4a-demo
1004

sudo/su 切换用户后,真实与有效同样一起切换,仍无差异:

$ sudo -u u4a-demo id
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)
$ sudo -u u4a-demo sh -c "id -u; id -ur; id -un; id -r -un"
1004
1004
u4a-demo
u4a-demo

真正让二者分道扬镳的是「只降有效身份、保留真实身份」的场景。用 util-linux 的 setpriv 把有效 UID 降到 1004、有效 GID 降到 1006,同时保留真实 UID=0(root):

$ setpriv --version | head -1
setpriv from util-linux 2.37.2
$ setpriv --euid=1004 --regid=1006 --clear-groups id
uid=0(root) gid=1006(u4a-demo) euid=1004(u4a-demo) groups=1006(u4a-demo)

注意 id 的默认格式在此场景自动多出一个 euid=1004(u4a-demo) 段——uid 段显示的是真实 ID 0(root)。此时 -n 与 -r -n 的对照最有说服力:

$ setpriv --euid=1004 --regid=1006 --clear-groups id -un
u4a-demo
$ setpriv --euid=1004 --regid=1006 --clear-groups id -r -un
root

同一进程,id -un 回答 u4a-demo(有效身份,权限判定用它),id -r -un 回答 root(真实身份,谁启动了进程)。-g/-gn/-gr -n 在该场景因 --regid 同时设定了真实与有效 gid 而输出一致:

$ setpriv --euid=1004 --regid=1006 --clear-groups id -gn
u4a-demo
$ setpriv --euid=1004 --regid=1006 --clear-groups id -gr -n
u4a-demo

💡 技术拓展:Unix 进程身份其实有三套 UID + 一组附属组,排查 setuid 程序与权限怪象时会全部碰上——

概念含义谁在用
真实 UID(RUID)进程属主的原始身份id -r -u 读取;ps、审计日志按它记账
有效 UID(EUID)内核做权限判定时用的身份id -u 读取;文件读写、kill 权限都看它
保存的 UID(SUID)进程保存的「可来回切换的原身份」仅供 setuid 程序在 RUID/EUID 间往返,id 不直接显示
附属组(Supplementary Groups)除主组外的额外组集合id -G/-Gn 读取;属组权限匹配按全集合判定

setuid 位程序(如 passwd)运行时 RUID=用户、EUID=root,正是 -r 与不带 -r 的差异所在。本节用 setpriv 构造等价场景实测复现了这一分离。

⚠️ 实测提醒:复现上述分离场景时,setpriv 的参数目标要直接是 id 本身(exec 一个新进程);若经过 Shell 中转(如 setpriv --euid=1004 ... bash -c 'id -u'),bash/dash 启动时会自动把有效 UID 复位回真实 UID,分离效果消失(实测 id -u 又返回 0)。

3.6 -Z SELinux 安全上下文:非 SELinux 内核的真实报错

-Z(--context)本意是只输出进程的 SELinux 安全上下文。Ubuntu 默认内核未启用 SELinux(Ubuntu 用 AppArmor),实测该选项直接报错:

$ id -Z
id: --context (-Z) works only on an SELinux-enabled kernel

确认报错时退出码为 1:

$ id -Z; echo "exit=$?"
id: --context (-Z) works only on an SELinux-enabled kernel
exit=1

-Z 与 -n 组合同样报此错(错误检查优先于参数组合校验):

$ id -Zn
id: --context (-Z) works only on an SELinux-enabled kernel

💡 要点:id: --context (-Z) works only on an SELinux-enabled kernel 是 Ubuntu 上的真实回显,不是环境故障。这条报错本身就是判断「内核是否启用 SELinux」的快捷手段之一。在启用了 SELinux 的系统(RHEL/CentOS/Fedora)上,同一选项会输出形如 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 的上下文。Ubuntu 用户做安全上下文查询应使用 AppArmor 工具链(aa-status 等)。

3.7 -a 兼容选项:加了等于没加

-a 在帮助里自述为 ignore, for compatibility with other versions——历史上某些 Unix 的 id 用 -a 表示「输出全部信息」,GNU id 的默认输出就是全量,故该选项被忽略。实测输出与默认完全一致:

$ id -a
uid=0(root) gid=0(root) groups=0(root)
$ id -a u4a-demo
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)

💡 要点:来自其他 Unix 的老脚本若带 id -a,在 GNU/Linux 上无需修改即可运行。写新脚本不必加 -a。

3.8 -z NUL 分隔符:面向 xargs 的安全输出

-z(--zero)把输出分隔符从空格/换行改为 NUL 字符(\0),帮助同时注明「not permitted in default format」——它只能搭配 -u/-g/-G 等单项模式使用。先验证默认格式下确实被拒绝:

$ id -z; echo "exit=$?"
id: option --zero not permitted in default format
exit=1

搭配 -Gn 使用时,用 od -c 把字节摊开看:普通输出以空格分隔、换行结尾:

$ id -Gn u4a-demo | od -c | head -3
0000000   u   4   a   -   d   e   m   o       u   4   a   -   g   r   p
0000020  \n
0000021

加 -z 后变为 NUL 分隔、无末尾换行:

$ id -Gnz u4a-demo | od -c | head -3
0000000   u   4   a   -   d   e   m   o  \0   u   4   a   -   g   r   p
0000020  \0
0000021

💡 要点:NUL 是 Unix 文件名唯一不允许出现的字符,id -Gnz 的输出因此可安全喂给 xargs -0。用户名合法时可含空格(如 john doe),此时按空格切分会撕碎名字,-z 是唯一可靠方案。常规场景无需 -z。

四、实战示例

本节给出几个生产与脚本中常见的组合场景,全部命令可直接复制执行。

4.1 脚本判 root:用数字 UID 而不是用户名

判断 root 的最稳写法是比较数字 UID,不依赖「root 这个名字」(UID 0 即超级用户,用户名理论上可改):

#!/bin/bash
if [ "$(id -u)" -ne 0 ]; then
    echo "错误:需要 root 身份,当前为 $(id -un)(uid=$(id -u))" >&2
    exit 1
fi
echo "uid=0 校验通过"

root 下执行通过;用 sudo -u 以 u4a-demo 身份运行验证拦截(演示脚本放 /tmp 以便普通身份可读):

$ bash /tmp/u4a-idgate.sh
uid=0 校验通过
$ sudo -u u4a-demo bash /tmp/u4a-idgate.sh; echo "exit=$?"
错误:需要 root 身份,当前为 u4a-demo(uid=1004)
exit=1

守门双向实测通过。报错行里顺带用 id -un 与 id -u 给出可读身份与数字身份,排障信息完整。

⚠️ 注意:验证这类守门时不要用 setpriv --euid=1004 bash 脚本 构造「半降权」场景——实测 bash 启动时会检测到有效 UID 与真实 UID 不一致,并自动把有效 UID 复位回真实 UID(setpriv --euid=1004 --regid=1006 --clear-groups bash /tmp/u4a-idgate.sh 仍输出 uid=0 校验通过),脚本里的 id -u 依旧返回 0。sudo/su 做的是全量切换(真实=有效一起变),不存在这个问题。

4.2 组成员检查:判断用户是否在指定组

授权核查常见问题:「u4a-demo 在不在 u4a-grp 组里?」用 id -Gn 加 grep -ow(按整词匹配,避免组名子串误判):

$ id -Gn u4a-demo | grep -ow u4a-grp && echo "u4a-demo 在 u4a-grp 组"
u4a-grp
u4a-demo 在 u4a-grp 组

反向检查一个不存在的组,命中 || 分支:

$ id -Gn u4a-demo | grep -ow nosuchgrp || echo "u4a-demo 不在 nosuchgrp 组"
u4a-demo 不在 nosuchgrp 组

脚本化封装就是一行函数:in_group() { id -Gn "$1" | grep -qw "$2"; },返回码即判定结果。改组成员后立即用该法复核(usermod -aG 对已登录会话不生效,需重新登录,见第五节 Q4)。

4.3 数字与名字双向转换

id 是 uid/gid 与名称互查的瑞士军刀,四个方向一次看全:

$ id -un 1004
u4a-demo
$ id -u u4a-demo
1004
$ id -gn u4a-demo
u4a-demo
$ id 1004
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)

分析日志里裸的数字 uid(如 auth.log、audit 记录)时,id -un <数字> 直接翻译成可读名字;id <数字> 则连组信息一并给出。注意方向性:id 的操作数位置只能放用户或用户数字 UID,放组名会报 no such user(3.2 已实测)。

4.4 sudo 提权前后身份快照

排障时把「切换前后」的身份快照放在一起对照,能快速定位权限类问题:

$ whoami; id
root
uid=0(root) gid=0(root) groups=0(root)
$ sudo -u u4a-demo sh -c 'whoami; id'
u4a-demo
uid=1004(u4a-demo) gid=1006(u4a-demo) groups=1006(u4a-demo),1007(u4a-grp)

sudo -u u4a-demo 后 uid、gid、groups 三段全部落到目标用户,且附加组 u4a-grp 一并生效——sudo 建立的进程身份是完整初始化的(等价于按 /etc/passwd、/etc/group 重新装配)。若此处 groups 缺少预期组,说明改组后未重新登录(见 Q4)。

4.5 附属组清单的机器可读处理

把某用户的全部组名转成逐行列表,供后续循环或比对:

$ id -Gn u4a-demo | tr " " "\n"
u4a-demo
u4a-grp

需要更严格的安全拼接时(用户名可能含空格的场景)用 -z 配合 xargs -0,输出字节用 od -c 验证:

$ id -Gnz u4a-demo | od -c | head -3
0000000   u   4   a   -   d   e   m   o  \0   u   4   a   -   g   r   p
0000020  \0
0000021

tr " " "\n" 版本适合交互查看;-z 版本适合管道进 xargs -0 做批量操作(如逐组 chown 校验),二者按场景选用。

五、注意事项与常见问题

Q1:id -un 和 whoami 有区别吗? 输出层面没有:id -un 与 whoami 都取当前有效 UID 对应的用户名,结果恒一致(whoami 帮助原文即写 Same as id -un.)。区别在能力面:id 还能带操作数查任何用户、输出 uid/gid/groups 全量信息,whoami 只能看自己。脚本里二者可互换;要更多信息一律用 id。

Q2:id -Z 报 --context (-Z) works only on an SELinux-enabled kernel 是坏了吗? 不是,这是 Ubuntu 的正确行为。Ubuntu 默认内核不启用 SELinux(安全模块是 AppArmor),-Z 无上下文可取,如实报错且退出码为 1(3.6 实测)。该报错反而可以当作「SELinux 是否启用」的探测手段。需要安全上下文信息时在 Ubuntu 上使用 aa-status 等 AppArmor 工具。

Q3:id <用户名> 和 id <数字> 都行,混用要注意什么? 两者都合法(3.2 实测 id 1004 与 id u4a-demo 等价)。但注意歧义风险:如果系统里同时存在名为 1004 的用户和 uid=1004 的用户,GNU id 的解析规则是先按用户名查找、找不到再按数字 UID 查找。生产脚本建议统一用用户名操作数,数字仅用于日志反查。

Q4:usermod -aG 加了组,id -Gn 却看不到新组? 组成员关系在登录时写入进程凭证。usermod -aG user 组 只改 /etc/group 数据库,已存在的登录会话(包括当前 Shell)不会自动获得新组——对旧会话执行 id -Gn 仍是旧列表。处理办法:退出重登,或用 newgrp 组名 在当前 Shell 换主组,或 su - 用户 起新会话后再 id -Gn 验证。判断「改组是否生效」永远以重新登录后的 id -Gn 输出为准。

Q5:为什么 -r 平时看不出任何差别? 因为绝大多数进程的 RUID 与 EUID 相同(普通程序、sudo/su 切换后的程序都是二者一致,见 3.5 实测)。只有 setuid 位程序运行时、或用 setpriv/系统调用显式只改有效身份时,二者才分离。遇到「ps 显示属主是 A、文件却以 B 权限写入」这类怪象时,正是 id -u 与 id -r -u 分头取证的时机。

Q6:id -z 单独用为什么报错? -z 只服务于 -u/-g/-G 系单项输出的分隔符替换;默认格式里 uid/gid/groups 三段有各自的语义标签,NUL 分隔会破坏格式,故 coreutils 直接拒绝并在报错文案里说明(id: option --zero not permitted in default format,3.8 实测 exit=1)。记住搭配规则:-z 必须与 -u/-g/-G(可再带 -n)同用。

六、参考链接