我有一次跟着一篇教程装软件,第一行就是 su root。
敲下去,输密码,报 鉴定失败。我以为记错了,又输了两遍,还是失败。换键盘布局、重启、甚至怀疑自己装系统时设的密码有问题——折腾了四十分钟。
最后才知道:Ubuntu 上的 root 账号默认是锁着的,根本没有密码可以输。 我在那儿拼命回忆的那个「root 密码」,从装完系统那天起就不存在。
这篇先把 root 和普通用户的关系讲清,再把 su、sudo 这一串长得差不多的命令摆在一起对照。看完你就知道:大部分教程让你敲的那条,其实不是最好的做法。
〇、先花两分钟:root 和普通用户是什么关系
登录一台 Linux,你其实是在「以某个用户的名义」操作。
你现在的名字,一行命令就能看到:
$ whoami
rainbow
你现在的身份编号,也是一行:
$ id
uid=1000(rainbow) gid=1000(rainbow) groups=1000(rainbow),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),111(lpadmin),114(lxd)
怎么读(这份输出后面还会反复用到,先花一分钟记住三件事):
| uid=1000(rainbow) | 你的用户编号是 1000,名字是 rainbow |
| gid=1000(rainbow) | 你的主组编号也是 1000 |
| groups=… | 你还属于哪些附加组(关键在下面) |
Linux 里每个账号都有一个编号,叫 UID。有两个编号是特殊的:
| 0 | root——系统里的最高权限账号 |
| 1~999 | 系统账号(Nginx、MySQL 这类服务专用,一般不允许登录) |
| 1000 起 | 普通用户——你自己 |
打个比方:root 是大楼的管理员,能进任何一个房间、能换锁、能拉电闸;普通用户是有自己房间钥匙的住户,只能进自己那间。你日常干活用住户钥匙就够了,只有真的要动线路、动承重墙(装软件、改系统配置、看别人的文件)时,才需要请管理员来一下。
su 和 sudo 这两族命令,解决的就是「怎么把管理员请过来」的问题——只不过两族人的思路完全不同:
-
su = 换人:直接变成 root,之后敲的所有命令都是 root 在执行
-
sudo = 代跑:不动你的身份,只让 root 帮你跑这一条命令,跑完就还回去
先记住这个区别,下面所有的混乱都从这里来。
一、五条命令摆一起:各自要谁的密码
命令长得像,其实分成两族。最直接的区分办法是看它问你要谁的密码:
| su | root 的密码 | root |
| su – | root 的密码 | root(完整登录环境) |
| sudo 命令 | 你自己的密码 | 还是你自己,只那一条命令以 root 跑 |
| sudo -i | 你自己的密码 | root(登录式) |
| sudo su | 你自己的密码 | root |
看这张表最后三行——只要前面带 sudo,问的就是你自己的密码。 这是新手最容易搞混的地方:教程里让你敲 sudo su,你却在拼命想 root 密码,永远敲不进去。
那 su 和 sudo su 结果都是变成 root,为什么还要有后一种?因为前一种在 Ubuntu 上第一步就卡死。
二、su 为什么在你的机器上多半走不通
先查一下 root 账号的状态。注意这条命令要看密码哈希,所以得带 sudo——普通用户跑不了:
$ passwd -S root
passwd:您不能查看或更改 root 的密码信息。
换个路子,直接看密码文件。/etc/shadow 只有 root 能读,第 2 段就是密码状态:
$ sudo awk -F: '$1=="root"{print $1, substr($2,1,3)}' /etc/shadow
root $y$
怎么读:第 2 段的开头几个字符决定一切。
| ! 或 * | 锁定 / 没有可用密码——su 永远进不去 |
| $y$ / $6$ / $1$ | 已设密码($y$=yescrypt、$6$=SHA-512、$1$=MD5)——只要密码对,su 就能进 |
Ubuntu 桌面版安装时根本不问你 root 密码,官方文档的说法是给 root 分配了一个「不匹配任何可能值的哈希」——直白讲就是锁着。所以下面这一幕在默认机器上是必然会发生的:
$ su root
密码:
su: 鉴定失败
但别急着下结论。 我这台机器上实测第 2 段是 $y$j9T$…(yescrypt),说明它设过 root 密码——这不是装完系统就有的状态。所以如果你在自己机器上看到的是 $y$ 或 $6$,那 su root 失败只意味着一件事:密码敲错了,和「账号被锁」完全是两码事。
想确认自己属于哪种,两条命令二选一:
$ sudo passwd -S root # 第二位:L=锁定,P=有密码,NP=无密码
$ sudo grep ^root: /etc/shadow # 第 2 段:! / * = 锁定,$y$ / $6$ = 有密码
(如果你查出来的是 L,下一步通常就是「那我 sudo passwd root 设一个不就行了」——能设,但多数情况下不该设,理由写在第七节的坑 2 里。)
正确的替代动作是这一条:
$ sudo -i
输你自己的密码,回车,提示符变成 root@…#,你就变成 root 了。
三、sudo 到底做了什么
sudo 的全称是 "superuser do"(以超级用户身份做)。它做三件事:
检查你有没有资格提权(看你在不在 sudo 组里);
问你要你自己的密码;
以 root 身份跑完你给的那一条命令,然后立刻归还权限。
它凭什么判断你有资格? 回到第〇节那份 id 输出里的 groups:
groups=1000(rainbow),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),111(lpadmin),114(lxd)
└──── 你自己 ────┘ └adm┘ └cdrom┘ └─sudo─┘ …
关键是 27(sudo) 这一项。
你之所以能敲 sudo,就是因为你在这个组里(Ubuntu 装系统时,会把第一个用户自动加进 sudo 组)。
顺带记住一个跨发行版的坑:RHEL / CentOS / Rocky 系里,这个组叫 wheel,不叫 sudo。 同样一条「把自己加进提权组」的教程,在 Ubuntu 上写 sudo,在 RHEL 上要写 wheel,抄错就是一个多小时。
看自己被允许做什么:
$ sudo -l
用户 rainbow 可以在 rainbow-VMware-Virtual-Platform 上运行以下命令:
(ALL : ALL) ALL
怎么读:这一行要拆成三段看——「在哪台机器上 : 以谁的身份 : 能跑哪些命令」。
(ALL : ALL) ALL 是三段全开:任何主机、任何身份、任何命令。
桌面机上这样很方便,生产服务器上这样很危险——等于给了一把万能钥匙。真要做安全配置,就得动 /etc/sudoers,那部分放在本专栏的 C4 里讲。
最后记住两条实用细节:
-
sudo 有一次 15 分钟的免密窗口:刚输完密码,短时间内再敲 sudo 不会再问;
-
敲 sudo -k 可以立刻作废这个窗口(公共机器上走人前顺手敲一下)。
最后一条,也是 sudo 比 su 更值得用的真正原因:它把每件事都记下来了。
$ sudo grep -a sudo /var/log/auth.log | tail -3
… sudo: pam_unix(sudo:session): session opened for user root(uid=0) by rainbow(uid=1000)
… sudo: rainbow : TTY=/dev/pts/0 ; PWD=/home/rainbow/桌面 ; USER=root ; COMMAND=/usr/bin/sh -c …
… sudo: pam_unix(sudo:session): session closed for user root
怎么读:一行里能看出谁(by rainbow(uid=1000))、在哪台终端(TTY=/dev/pts/0)、在哪个目录(PWD=)、以谁的身份(USER=root)、跑了什么(COMMAND=)。
su 按设计就是「换个人」,日志里只留一句「有人变成 root 了」,之后干了什么一概查不到——当一台机器不止一个人在用,「能不能追责」比「方不方便」重要得多。
四、真机对照:sudo 之后,环境变没变
这一节是本文最有用的一段——很多人踩的坑不在「能不能提权」,而在「提权之后环境变了」。
把同一个问题分别问给「普通用户」和「sudo」:
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
$ sudo printenv PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
怎么读:把两条逐段对齐看——
| /usr/local/sbin | ✔ | ✔ |
| /usr/local/bin | ✔ | ✔ |
| /usr/sbin | ✔ | ✔ |
| /usr/bin | ✔ | ✔ |
| /sbin | ✔ | ✔ |
| /bin | ✔ | ✔ |
| /usr/games | ✔ | ✘ 没了 |
| /usr/local/games | ✔ | ✘ 没了 |
| /snap/bin | ✔(出现两次) | ✔(一次) |
sudo 把 PATH 整个换掉了,不是往后追加。
这不是随机的,是 /etc/sudoers 里一行配置写死的:
$ sudo grep -n secure_path /etc/sudoers
11:Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
怎么读:secure_path 的意思是——为了安全,sudo 不走你的 PATH,只走我指定的这条。防的是「你在自己目录里放个假 ls,然后忽悠 root 去执行」。
代价就是:你自己装的、放在 /usr/local/games 或某些自定义路径下的命令,sudo 之后会突然「找不到」。
(这正好是《报错排查 06》里第 3 种成因的现实版本。当时我们只知道「sudo 会重置 PATH」,现在你看到了它是在哪一行、怎么重置的。)
$ cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
怎么读:这才是你那条 PATH 的「源头」——登录时由系统读进来。对比 sudoers 那行,多的正好是 /usr/games、/usr/local/games。两份配置、两套 PATH,这就是差异的全部来源。
再补一刀:sudo 换掉的不只是 PATH,还有 HOME——但不换当前目录:
$ pwd
/home/rainbow/桌面
$ sudo sh -c 'echo HOME=$HOME; echo pwd=$(pwd)'
HOME=/root
pwd=/home/rainbow/桌面
怎么读:HOME 已经变成 /root,可 pwd 还停在 /home/rainbow/桌面。
两条合起来就说明了 sudo 的本质——它不是「换人」,是「代跑」:只把几个环境变量(PATH、HOME)按 root 的配置重置一遍,进程所在的目录原封不动。
所以有个经典事故:你 cd 到自己的项目目录,接着 sudo 某命令——命令是以 root 跑的,但落在你原来的目录里,不是 /root。反过来同样坑:sudo 之后 ~ 指的是 /root,sudo echo x > ~/a.txt 写的是 /root/a.txt 而不是你的家目录。这就是下一节坑 4 的另一个变体。

五、su 和 su -:差在那个减号
如果你哪天在一台有 root 密码的机器(比如 RHEL 系、或者你手动给 root 设过密码)上敲 su,会立刻遇到第二个坑:su 和 su – 不一样,差的不是一点点。
su → 换身份,但保留你当前的大部分环境
su – → 换身份 + 完整走一遍 root 的登录流程(等价于 su -l / su –login)
具体差在哪,敲两条命令就现形了:
| pwd(在哪个目录) | 还停在你原来的目录 | 进 /root |
| echo $HOME | 还是 /home/你的用户名 | /root |
| echo $PATH | 基本还是你那套 | root 那套 |
| 提示符 | 常常还是 $(不变 #) | # |
为什么这很坑:你用 su 进去,看着提示符是普通用户的样子,其实权限已经是 root 了——你以为自己在自己家目录里干活,实际上一抬手删的是 root 的东西。或者反过来:你以为已经「切到 root 环境」了,结果 pwd 还在别人目录下,一跑脚本路径全错。
一句话记法:要变成 root 干活,就敲 su -(带减号);不带减号的 su 是「只换身份、不动环境」,日常几乎不该用。
那 sudo su 是什么?就是用 sudo 的密码机制去执行 su——问你的密码、不用 root 密码。它等价于一次「保留环境的提权」,所以常见的老教程写:
sudo su ≡ 用你自己的密码,变成 root(保留环境)
sudo su – ≡ 用你自己的密码,变成 root(完整登录环境)
sudo -i ≈ sudo su – (推荐写法,更短、更标准)
结论:想在 Ubuntu 上变成 root,敲 sudo -i。老教程里的 su root 在这台机器上永远进不去,sudo su 能进去但啰嗦。
六、决策表:什么场景用哪个
| 装软件、重启服务、改一个系统配置文件 | sudo 命令 | 只借这一次,风险最小 |
| 要连着敲十几条 root 命令(比如配环境) | sudo -i | 一次进入 root 登录环境,路径和别名都对 |
| 只想保留自己环境,但让某条命令以 root 跑 | sudo 命令 或 sudo -E 命令 | 环境不变,行为可预期 |
| 教程让你 su root | 换成 sudo -i | Ubuntu 上 root 是锁的,su 进不去 |
| 要看一下自己被允许提权到什么程度 | sudo -l | 安全体检的第一步 |
| 走人前想作废免密窗口 | sudo -k | 别人接手这台机器时不会白捡 root |
七、四个坑
坑 1:以为 su 和 su – 差不多。 差在环境:su 保留你的目录、HOME、PATH,su – 才是真正的 root 登录环境。用 pwd 和 echo $HOME 一比就知道。带减号的那个才是「变成 root」。
坑 2:为了能用 su,给 root 设一个密码。
sudo passwd root # ← 能设,但先看完这段再决定
设完确实能 su 了,代价是:这台机器从此多了一个可以被密码猜的 root 账号。Ubuntu 默认锁着 root,正是为了堵死这条路;一旦设了密码,如果还开了 SSH 密码登录,暴力破解的目标就从「没密码的 root」变成了「有密码的 root」。
要提权就 sudo -i,不需要给 root 设密码。
坑 3:在 root 里再套一层 sudo。 sudo -i 之后你已经就是 root 了,再敲 sudo 纯属多余;在某些没有终端的环境(脚本、CI)里还会直接报 sudo: a terminal is required to read the password。
坑 4:sudo echo … > /root/文件 写不进去。
这条最隐蔽。看这个例子:
$ sudo echo "hello" > /root/test.txt
bash: /root/test.txt: 权限不够
为什么加了 sudo 还说权限不够? 因为这条命令实际干的事有两步:
sudo echo "hello" —— 这一步确实是 root 在跑;
> /root/test.txt —— 这个重定向是「你的 shell」做的,不是 root 做的。
shell 拿着你的身份去开 /root/test.txt,权限自然不够。sudo 只管住了命令,没管住重定向。
正确写法是让「写文件」这个动作也被 root 执行:
$ echo "hello" | sudo tee /root/test.txt
hello
怎么读:tee 的作用是「把标准输入同时写到文件和屏幕」,所以它既完成了写入,又把内容回显出来(上面那个 hello 就是它打印的)。记住这个组合:要往只有 root 能写的地方写文件,用 | sudo tee 文件,不要用 sudo … >。
速查表(文末收藏版)
我现在是谁 → whoami / id
看 root 有没有密码 → sudo passwd -S root (第二位 L=锁定,P=有密码)
sudo grep ^root: /etc/shadow (第 2 段:! 锁 / $y$·$6$ 有密码)
变成 root(Ubuntu) → sudo -i (要自己的密码)
变 root(有 root 密码)→ su – (带减号才对)
只看提权权限 → sudo -l
提权后立刻收回 → sudo -k
sudo 的 PATH 从哪来 → sudo grep -n secure_path /etc/sudoers
你的 PATH 从哪来 → cat /etc/environment
sudo 后命令找不到 → 多半是 secure_path 里没有那个目录
往 root 目录写文件 → echo 内容 | sudo tee 文件 (不能用 sudo … > )
属于哪个提权组 → Ubuntu: sudo 组 / RHEL: wheel 组
提权日志在哪 → /var/log/auth.log


