1. su 命令的使用场景与设计思路
1.1 su 到底解决什么问题
先聊一个几乎所有 Linux 使用者都会撞上的场景:你用普通用户登录了一台服务器,正准备重启某个服务,结果系统甩给你一行Permission denied。这时候你才意识到,日常操作受限明显。而su命令,全称 switch user,就是用来在当前登录会话中切换身份的工具。它的核心价值很简单:让你从一个用户身份切换到另一个用户身份,最常见的用法就是切到 root。
有人可能会觉得,那我直接用 root 登录不就完事了?问题恰恰出在这里。以 root 身份长时间操作,等于把系统最高权限暴露在整个会话过程中。你不小心敲错一条rm -rf,或者某个脚本里带着变量为空路径执行清理,代价就是整台服务器的数据。所以稍微正规一点的服务器环境,都会禁止直接 root 登录,或者至少建议你不要这么做。用普通用户日常干活,需要高权限操作时再通过su切过去,办完事立刻退出,这个习惯可以从源头减少大量低级失误。这就是 su 存在的底层逻辑——为“临时提权”提供一个受控的入口。
本文适合刚接触 Linux 的新手,也适合正在梳理运维规范的从业者。跟着走一遍,你会明白 su 和 su - 的差别、如何切换到指定用户、如何用 su 执行单条命令,以及那些看起来莫名其妙的报错到底该怎么排查。整篇文章以实操为主线,命令都能在你自己的测试机上跑一遍。
1.2 su、sudo、ssh 三兄弟怎么分工
在真实工作环境中,su 并不是唯一跟“身份切换”相关的命令。经常有人把 su、sudo、ssh 混在一起,其实它们的分工差异巨大,用错了场景就会踩坑。
先看 ssh。它的作用是建立远程连接,在你登录进系统之前就已经完成身份认证。也就是说,ssh 解决的是“从外部进入系统”的问题,而 su 解决的是“进入系统之后切换身份”的问题。两者在时间线上有先后关系。
再对比 su 和 sudo。sudo 是“以某个用户的权限执行单条命令”,它依赖/etc/sudoers文件做细粒度授权,你可以精确控制某用户能执行哪些命令、不需要完整密码等。su 则是把当前 shell 整个切换到目标用户,是一个持续的交互式会话。它们的核心区别可以这样理解:sudo 像给特定钥匙开了特定锁,su 则是直接换了一整套身份。
实际使用中,如果只是偶尔执行一条管理命令,优先考虑 sudo;如果需要长时间以另一个用户的身份工作,或者目标用户是某个服务专用账号(数据库账号、应用账号),su 更顺手。下面这张表可以帮你快速决策。
| 对比项 | su | sudo | ssh |
|---|---|---|---|
| 核心用途 | 切换用户身份 | 单条命令提权 | 远程登录系统 |
| 认证方式 | 目标用户密码 | 当前用户密码(或免密) | 密码或密钥 |
| 授权粒度 | 全量切换 | 按命令细粒度授权 | 按主机和用户控制 |
| 典型场景 | 切到 root 做维护 | 普通用户执行管理命令 | 从外部登录服务器 |
1.3 最小权限原则下的部署思路
你可能会问,既然 sudo 看起来更安全,为什么还要学 su?因为很多场景下 su 反而更直接。比如某些服务器维护窗口,运维人员需要长时间在 root 状态下检查日志、调试配置,用 sudo 一条条命令加前缀既繁琐又容易漏;再比如切换到业务账号启动服务时,业务账号的密码你可能并不掌握,需要 root 先用 su 切过去再操作。工具不分好坏,关键是在合适的场景用合适的方式。
我个人的建议是守住一条底线:能用普通用户完成的事,绝不进入 root 会话;必须提权时,优先用 sudo 或 su -c 执行单条命令;确实需要交互式 root 会话时,办完事立刻exit。这套思路就是最小权限原则的日常化版本,后面所有实操都会围绕这条底线来展开。
2. su 命令的核心参数逐个拆解
2.1 绕不开的 su 与 su - 区别
先说最重要的一点:su和su -不是同一个东西,差一个减号,行为差出十万八千里。很多新手刚接触时都在这上面栽过跟头。
su username执行的是非登录式切换。它把当前用户切换为目标用户,但保留你原来会话中的大部分环境变量,比如当前目录、PATH、用户自定义的变量等。听起来好像挺方便,可是实际使用时问题很大。你在普通用户下 PATH 里没有/sbin和/usr/sbin,切到 root 后发现连ifconfig、systemctl都找不着;或者切换前设置了某个临时变量,切换后它还在干扰程序运行。这些“残留环境”造成的报错特别难排查。
su - username才是完整的登录式切换。它会模拟一个全新的登录流程:读取/etc/profile、~/.bash_profile等初始化文件,重新设置 PATH、HOME、SHELL 等关键环境变量,并把当前目录切到目标用户的 home 目录。用大白话说,su -让你“彻底变成目标用户”,而不是“顶着一张目标用户的皮,内里还是旧环境”。
做个实验你就清楚了。普通用户执行su root后执行echo $PATH,大概率会看到一串包含普通用户目录的路径;执行echo $0,通常显示的也是切换前那个 shell 的名字(比如 bash)。而执行su - root后,echo $0会显示-bash,这代表它已经是一个登录 shell,环境是全新初始化的。判断一个 shell 是否为登录 shell,echo $0是常用手段。
2.2 常用参数速查与适用场景
su 命令的参数不算多,但每一个都有明确的使用场景。我整理了最常用的几个,你可以直接对照参考。
| 参数 | 完整写法 | 作用 |
|---|---|---|
- | su - username | 登录式切换,重新初始化环境变量 |
-l | su -l username | 同-,显式指定登录式切换 |
-c | su - username -c "命令" | 以目标用户身份执行单条命令后退出 |
-s | su -s /bin/sh username | 指定切换后使用的 shell |
-p | su -p username | 保留当前环境变量,不重新初始化 |
-m | su -m username | 同-p,保留环境变量 |
-c是我在日常运维中用得最多的参数。它允许你以另一个用户的身份执行一条命令,执行完自动回到原身份,不需要进入交互式窗口。比如切到 tomcat 用户启动服务、或者以特定用户身份测试配置文件,用-c都极其顺手。-s则适合那些默认 shell 有问题、需要临时换 shell 的场景,比如目标用户的/etc/passwd里指定了/sbin/nologin,而你确实需要切过去执行某些操作时,可以用-s /bin/bash强制指定一个可用 shell。
2.3 交互式与一次性执行怎么选
使用 su 时,你首先要问自己一个问题:接下来要做的事,是需要长时间待在这个用户下,还是只执行几条命令?
如果是排查问题、调整服务配置这类需要反复查看、多命令组合的活,选择交互式切换,也就是su - username后慢慢操作。这样环境变量统一,tab补全也能正常工作,效率最高。如果只是需要以某个用户身份跑一个命令,比如查看某个服务账号能读到哪些文件,或者临时启动一个测试进程,那用su - username -c "命令"更合适。它把提权动作收束在单条命令里,执行环境干净,也不容易留下一个忘记退出的 root 窗口。
顺带提醒一下:如果你在命令中使用了管道或重定向,比如su - root -c "cat /etc/shadow | grep root",注意引号要用对。外层建议使用双引号包住整条命令,内层如果需要字符串,用单引号。否则 shell 可能会在修剪引号时把命令拆得支离破碎,出现各种奇怪的 syntax error。
3. 实操过程与核心环节实现
3.1 从普通用户切换到 root
先看最基础也最常用的操作:普通用户直接切到 root。假设你当前登录用户是某开发者,终端提示符是$。
su -回车后系统提示你输入 root 的密码。注意一点:终端上不会显示星号或者圆点,连光标都不动,这不是死机,是正常现象。很多新手在这里反复回车,以为密码没输进去,实际上密码已经收到了。输入正确后,提示符会变成#,执行whoami会输出root,执行pwd会显示/root。这台机器现在已经进入 root 会话。
加个细节:su -和su root -是等价的,root 是默认的切换目标。也就是说直接写su -就等同于切换到 root。如果你想切换成其他用户,就必须把用户名写清楚,例如su - tomcat。
切换完成后,你的当前目录已经变成了新用户的 home 目录,并且环境变量已刷新。验证方式很简单:执行echo $HOME,如果输出的是/root,说明环境初始化成功。接下来你可以在这个会话里自由执行需要 root 权限的管理操作。
3.2 切换到指定业务用户
真实工作中,你更多情况下切换的不是 root,而是各种业务账号。举个例子:某公司有一套 Java 应用,跑在app_user这个账号下。你现在以 root 身份登录了服务器,想看看这个应用日志,但日志目录的权限只对app_user开放。直接操作会因为权限不足报错,这时候就需要切到app_user。
su - app_user输入 app_user 的密码后,你就进入了该用户的主目录/home/app_user。在这个环境下,所有读写操作都以 app_user 的身份执行,访问日志或者启动脚本都不会再吃权限的闭门羹。用这种方式还能顺带验证应用账号对特定目录是否有读写权限,因为它模拟的正是应用运行时真实的权限环境。
补充一个常见误区:su切换时输入的密码是“目标用户”的密码,不是当前用户的密码。很多人在切到别的用户时习惯性输入 root 密码,结果一直报Authentication failure。如果你不知道目标用户的密码,只能用 root 先执行passwd 目标用户名来重置,然后才能成功su。
3.3 用 su 执行单条管理命令
有时你需要的只是一个 root 权限的命令,比如重启 Nginx、重载 systemd 服务,或者查看一个只有 root 能看到内容的目录。进入交互式 root 会话就显得杀鸡用牛刀了。这种情况用-c参数最利索。
su - -c "systemctl restart nginx"这条命令的含义是:以 root 身份、登录式环境、执行systemctl restart nginx,执行结束后立刻退回当前用户。其中第一个-表示登录式初始化,第二个-c表示执行单条命令。如果你需要指定切换对象,可以把命令改成下面这种形式:
su - app_user -c "whoami && pwd"先切到 app_user,执行whoami确认身份,再执行pwd确认目录。注意&&会把两条命令串起来,前者成功才执行后者。这个组合在排查账号权限、验证服务账号环境时特别管用。
3.4 脚本中使用 su 的安全姿势
在 Shell 脚本里用 su 执行提权操作,是比较常见的自动化需求。很多人第一次写的时候,会尝试通过管道把密码喂给 su,比如下面这段写法:
echo "密码" | su - -c "systemctl restart nginx"这种写法有两个问题。第一,密码出现在脚本里,任何人只要拿到脚本就能看到明文密码;第二,即使密码正确,su 在检测到非终端输入时也经常拒绝认证,报must be run from a terminal。所以这条命令在实际环境中基本跑不通,而且跑通了也不应该用。
更稳妥的方案是借助 sudo 的免密配置。在/etc/sudoers中给某个账号配置NOPASSWD权限后,脚本里直接写:
sudo systemctl restart nginx这样无需密码,权限由 sudoers 统一控制。如果你确实必须使用 su,技术上可以通过expect这类工具来驱动交互式输入,但维护成本高,而且容易引入新的安全隐患。日常自动化建议优先走 sudo。
4. 常见问题与排查技巧实录
4.1 这些报错我几乎都遇到过
把我在实际使用中高频撞见的 su 报错整理成一个速查表,方便你直接对照。
| 报错信息 | 可能原因 | 解决方案 |
|---|---|---|
su: Authentication failure | 密码错误,或目标用户没有密码 | 用 root 执行passwd 用户重置密码 |
su: user xxx does not exist | 用户名拼写错误或用户不存在 | 用id 用户名确认用户是否存在 |
su: cannot open session: Permission denied | PAM 配置限制,或用户 shell 为 nologin | 检查/etc/pam.d/su,或更换 shell |
su: warning: cannot change directory to /home/xxx | 用户 home 目录缺失 | 用mkdir重建目录并chown授权 |
bash: command not found | 切换后 PATH 未初始化 | 使用su -重新加载环境变量 |
must be run from a terminal | su 在非交互式环境下执行 | 改用 sudo,或借助终端模拟工具 |
先重点说说Authentication failure,这几乎占了 su 报错的一大半。首次登录 Linux 服务器时,root 可能没有设置密码,或者密码是随机生成的一长串。这个时候你不能直接用 su 切换到 root,而是要先以当前账号通过 sudo 执行sudo passwd root给 root 设置一个明确的密码,之后再使用 su 才能成功。另外提醒一下,部分发行版默认禁止 su 直接切 root,比如 Ubuntu 的pam_auth配置就要求用户必须属于sudo组才可以,这种限制在/etc/pam.d/su里有明确记录。
4.2 排查思路从哪几个方向入手
遇到 su 相关的问题,建议按照下面这个顺序排查,别一上来就瞎试。
第一步,看日志。多数系统会把认证记录写到/var/log/auth.log(Debian/Ubuntu)或/var/log/secure(CentOS/RedHat)。打开日志查找su关键字,里面会写清楚认证失败的具体原因,比如密码错误、用户锁定或 PAM 拒绝。日志比报错信息本身可靠得多。
第二步,看用户。执行cat /etc/passwd | grep 用户名确认用户的 shell 是什么、home 目录是否合理。如果 shell 是/sbin/nologin或者/bin/false,那即使密码正确,su 也会拒绝切换,因为系统不允许这个用户登录。可以用usermod -s /bin/bash 用户名修改。
第三步,看 PAM。/etc/pam.d/su中如果有pam_wheel.so相关的配置,意味着只有属于wheel组的用户才能切换到 root。如果当前用户不在这个组里,必然报权限拒绝。解决方法是把用户加入wheel组,或者注释掉对应的 PAM 行。
第四步,看环境。如果 su 切换成功但命令没法用,第一反应就是环境变量有问题。用su -而不是su,迫使系统重新读取初始化文件。顺便检查一下/etc/profile和/etc/bashrc里有没有语法错误,文件里面有 bug 会导致环境变量加载异常。
4.3 用 su 之后一定要养成的检查习惯
切完用户之后,别急着敲命令。先花五秒钟确认三件事:身份对不对、目录对不对、环境对不对。
whoami pwd echo $HOME敲完这三个命令,你就知道自己有没有真正“变成”目标用户。尤其是echo $HOME,这是检验登录式切换是否生效的标志。如果你执行su tomcat后echo $HOME还是/root,说明你是非登录式切换,环境变量并没有刷新。这种状态下跑脚本,非常容易出现“脚本里默认路径错误”的问题。
还有一个容易被忽视的细节:su -切换后,工作目录是目标用户的 home 目录。如果你在原目录下有一些相对路径相关的操作,切换到新用户后路径就失效了。我在一次部署中切到应用账号后想读取./config,结果读了半天读不到,就是因为工作目录已经变了。从此我养成了一个习惯:切换身份后的第一个命令永远是pwd。
另外一个经验之谈是:进入 root 会话后记住敲exit而不是直接关闭终端。exit会干净退出当前用户上下文,回到切换前的身份。直接关终端也不是不行,但会给审计留下一个“root 会话被异常终止”的记录。在重视审计日志的环境里,干净退出是对自己的一种保护。
最后分享一个我常用的验证小技巧。如果你想确认自己当前处于登录式 root 还是非登录式 root,执行:
echo $0如果输出是-bash,说明现在是登录式 shell;如果输出是bash,说明是非登录式。这个小命令不会产生任何副作用,却能让你瞬间明白自己踩的是哪种环境。我在排查环境变量问题时,几乎每次都用它来定位问题源头。