You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

手动SSH与脚本内SSH调用为何使用不同的zsh?

问题

我有一个deploy.sh文件,希望从本地MacBook运行它,通过ssh连接到远程Ubuntu 22服务器并在那里执行一些命令,示例如下:

#!/opt/homebrew/bin/zsh

main() {
  scp \
    on_server.sh \
    # other files \
    "$remote_location:/correct/location"

  ssh $remote_location "cd correct/location; echo $SHELL; ./on_server.sh"
}

main "$@"

on_server.sh文件包含几个使用pnpm和pm2的命令:

#!/usr/bin/zsh

pnpm i
pnpm build

pm2 restart app_name

如果我手动ssh登录服务器,cd到对应目录并执行这些pnpm和pm2命令,一切正常。但当deploy.sh执行看似相同的操作时,我会收到pnpm和pm2命令未定义的错误。

不过我注意到两种情况下的$SHELL值不同:
手动ssh时:

ssh <remote_location>
...
echo $SHELL
/usr/bin/zsh

而上述on_server.sh中echo $SHELL的输出为:

/bin/zsh

我认为$SHELL的差异是命令执行失败的原因,想知道为何$SHELL值会存在这种差异?


原因与解决方法

1. $SHELL差异的本质

你看到的$SHELL差异,核心是交互式登录shell与非交互式非登录shell的区别:

  • 手动ssh登录时,系统启动的是交互式登录shell,此时会读取你用户目录下的配置文件(比如~/.zprofile、~/.zshrc),$SHELL变量会指向/etc/passwd中为你配置的默认shell路径(也就是/usr/bin/zsh)。
  • 通过ssh remote "command"执行命令时,启动的是非交互式非登录shell,此时$SHELL会继承ssh会话的默认环境值(这里是/bin/zsh),更关键的是:这种模式下不会加载~/.zshrc或~/.zprofile这类用户配置文件。

2. 命令找不到的核心原因

pnpm和pm2这类全局安装的工具,其执行路径通常是在~/.zshrc或~/.zprofile中添加到PATH环境变量里的。非交互式shell不加载这些配置,导致PATH中没有包含工具的路径,自然会报“命令未定义”的错误。

3. 修复方案

可以通过以下几种方式解决:

  • 方案一:在远程命令中显式加载配置文件
    修改deploy.sh中的ssh命令,强制加载zsh的配置文件:

    ssh $remote_location "cd correct/location; source ~/.zshrc; ./on_server.sh"
    

    如果配置写在~/.zprofile里,就替换成source ~/.zprofile。

  • 方案二:在on_server.sh中使用工具的绝对路径
    先手动登录服务器,用which pnpm和which pm2拿到工具的绝对路径,然后修改on_server.sh:

    #!/usr/bin/zsh
    
    /path/to/pnpm i
    /path/to/pnpm build
    
    /path/to/pm2 restart app_name
    
  • 方案三:让ssh以登录shell模式执行命令
    通过zsh的-l参数强制启动登录shell,自动加载配置文件:

    ssh $remote_location "zsh -l -c 'cd correct/location; ./on_server.sh'"
    

内容的提问来源于stack exchange,提问作者Jackson H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 19:07:19