最可靠的当前 shell 判断方式是 ps -p $$ -o comm=,它直接读取内核中当前 shell 进程的真实可执行文件名,不受环境变量篡改、脚本调用名伪装或登录配置干扰,输出纯命令名(如 bash、zsh),适合脚本判断。

ps -p $$ -o comm= 是最可靠的当前 Shell 判断方式
它直接读取内核中当前 shell 进程的真实可执行文件名,不受环境变量篡改、脚本调用名伪装或登录配置干扰。
常见错误现象:ps | grep $$ 会混入 grep 自身进程,输出不可靠;ps -ef | grep $$ 同样容易匹配到无关行。
-
ps -p $$ -o comm=输出纯命令名(如bash、zsh、dash),无路径、无空格,适合脚本判断 - 在子 shell 中(如
(cd /tmp; ps -p $$ -o comm=))仍能准确反映该子 shell 的实际解释器 - 容器或 CI 环境中,即使
$SHELL被设为/bin/sh,该命令也能暴露真实运行的是dash还是bash
echo $0 在交互式会话中够用,但脚本里会失效
$0 是当前 shell 或脚本的调用名,轻量且快,但语义不统一:交互式下通常是 shell 名(bash),而执行脚本时变成脚本路径(./deploy.sh)。
容易踩的坑:直接用 echo $0 写自动化检测逻辑,在脚本中会误判为“当前 Shell 是 deploy.sh”。
- 若坚持用
$0,务必配合basename "$0"提取命令名 - 注意
exec -a myshell bash类操作会篡改$0,此时$0显示myshell,但真实进程仍是bash - 在管道或
ssh user@host 'command'场景中,$0可能显示-bash(带短横表示 login shell),而ps -p $$ -o comm=仍输出bash
$SHELL 只反映默认登录 Shell,不是当前正在跑的那个
$SHELL 的值来自 /etc/passwd 第 7 列,只在用户登录时读取一次,后续手动切换(如 zsh、exec fish)完全不会更新它。
典型误判场景:你在 bash 里执行 zsh 进入新会话,echo $SHELL 仍输出 /bin/bash,但所有命令实际由 zsh 解析。
- CI/CD 脚本中依赖
$SHELL判断语法兼容性(比如是否支持[[)必然出错 -
chsh -s /usr/bin/fish修改后,必须重新登录才生效;当前会话的$SHELL不变 - 普通用户无法写入
$SHELL,它是只读环境变量,修改无效
cat /etc/shells 查的是“系统允许设为登录 Shell”的列表,和当前无关
这个文件定义的是 PAM 和 chsh 的白名单,跟“此刻终端里跑的是什么”毫无关系。有人看到 /bin/sh 在里面,就以为当前一定是 POSIX 兼容 shell,这是常见混淆点。
关键差异:你手动执行 /opt/myshell,ps -p $$ -o comm= 能看见它,但如果它没写进 /etc/shells,你就不能用 chsh -s /opt/myshell 设为登录 shell。
-
cat /etc/shells每行是一个绝对路径,普通用户可读,无需 sudo -
chsh不带参数会自动打印这个列表,本质就是读该文件;chsh -l在 Debian/Ubuntu 可用,RHEL/CentOS 不支持 -
/bin/sh几乎从来不是原始 Bourne shell,用readlink -f /bin/sh才知道它实际指向dash还是bash
真正要确认「此刻这行命令由谁解析」,唯一可信的是 ps -p $$ -o comm=。其他方法要么反映历史配置,要么依赖易被覆盖的变量,要么语义模糊——尤其在自动化、容器、SSH 远程执行等场景下,这点差异常导致脚本静默失败。











