在 Linux 運維與腳本編寫中,su 與 su -(或 su -l)的區別是導致「命令找不到」、「環境變量錯誤」、「工作目錄異常」等高頻問題的根源。本文通過原理分析、場景演示、腳本陷阱解析,助你徹底掌握用戶切換的正確姿勢。
| 特性 | su user | su - user / su -l user |
|---|---|---|
| 登錄 Shell 類型 | 非登錄 Shell | 登錄 Shell |
| 讀取配置文件 | 僅 ~/.bashrc | /etc/profile → ~/.bash_profile → ~/.bashrc |
| 環境變量 | 保留原用戶環境(PATH, HOME 等) | 重置為目標用戶完整環境 |
| 工作目錄 | 保持原目錄不變 | 切換至目標用戶 $HOME |
| 適用場景 | 臨時執行單條命令、保持上下文 | 完整切換用戶身份、運行服務、執行腳本 |
[root@db ~]# su oracle
[oracle@db ~]$ sqlplus / as sysdba
-bash: sqlplus: command not found
# 原因:保留了 root 的 PATH,未讀取 oracle 用戶 .bash_profile 中的 ORACLE_HOME/bin
修正:su - oracle
# 錯誤寫法
su load <
su:調用 pam_authenticate() 驗證密碼 → setuid() 切換 UID/GID → execve("/bin/bash", ["-bash"], envp),**不清理環境變量**,argv[0] 為 -bash(非登錄 Shell 標誌)。su -:同上,但 execve("/bin/bash", ["-bash", "-l"], envp) 傳遞 -l 參數,Bash 檢測到登錄 Shell 標誌,依次讀取 /etc/profile、~/.bash_profile、~/.bashrc,**重置 HOME、SHELL、USER、LOGNAME、PATH** 等關鍵變量。# 使用 expect 或 sshpass
expect -c "
spawn su - load
expect \"Password:\"
send \"$PASSWD\r\"
expect \"\\$\"
send \"cd ~/backup && rm *.txt\r\"
send \"exit\r\"
expect eof
"
# /etc/sudoers.d/app-deploy
appuser ALL=(load) NOPASSWD: /opt/scripts/cleanup_backup.sh
# cleanup_backup.sh (屬於 load:load, 750)
#!/bin/bash
cd ~/backup
rm -f *.txt
# 執行
sudo -u load /opt/scripts/cleanup_backup.sh
[Service]
User=load
Group=load
WorkingDirectory=/home/load/backup
ExecStart=/bin/rm -f *.txt
Type=oneshot
「帶橫杠,換環境,回家目錄;不帶橫杠,留舊環境,原地待命。」
su - user。su user 可接受。su,改用 sudo、systemd、ssh、容器化方案。