是一个非常经典的 SSH TTY(终端)争夺问题。
简单直接的回答是:去掉 -t 参数,或者把 -t 改成 -T (大写)。
🤔 为什么会出现这个问题?
当你使用 ssh -t 时,你是在强制 SSH 分配一个 伪终端(Pseudo-TTY) 并将其锁定在当前会话上。
在两层跳板(A -> B -> C)的场景下:
1. 第一层(A -> B):你按下了回车,A 把你的键盘(stdin)和屏幕(stdout)通过 -t 强行挂载到了 B 上。
2. 第二层(B -> C):B 上的 SSH 命令试图通过 C 执行命令。
3. 卡住的原因:因为 -t 抢占了标准输入(stdin),导致第二层的 SSH 无法正常读取到“执行完命令就退出”的信号。C 以为你是一个交互式用户登录进来了,所以它把 Shell 丢给你后就挂起等待你输入命令,而不是执行完命令自动退出。

✅ 正确的解决方法
方案 1:去掉 -t (最推荐)
如果你只是执行命令,不需要交互式终端,完全不需要加 -t 。
# 错误:强制分配终端,导致卡住
ssh -t user@jump1 "ssh -t user@target 'your_command'"
# 正确:不加 -t,让命令执行完自动退出
ssh user@jump1 "ssh user@target 'your_command'"
方案 2:显式禁用终端(使用 -T )
如果你必须写脚本,为了防止本地环境干扰,可以显式禁用 TTY。
ssh -T user@jump1 "ssh -T user@target 'your_command'"
方案 3:如果必须使用 -t (极少数情况)
只有在你需要交互式操作(比如执行完命令后还要手动敲代码)时才用 -t 。但如果是两层跳板且想自动退出,必须使用 -tt (两个 t)来强制分配并立即释放。
# 强制分配终端,但执行完命令后强制关闭连接
ssh -tt user@jump1 "ssh -tt user@target 'your_command'"
注意: -tt 通常用于自动化脚本中,当目标命令不需要交互但又必须分配 TTY 时(例如运行 sudo 或 docker exec -it )。
🛠️ 针对你之前代码的修改建议
结合你之前的脚本,如果你是通过跳板机去连接目标服务器,千万不要在执行命令的 SSH 中加 -t 。
错误写法(会导致卡在跳板机):
ssh -t "${user}@${server}" "$(declare -f remoteCMD; remoteCMD)"
正确写法(自动执行完退出):
ssh "${user}@${server}" "$(declare -f remoteCMD; remoteCMD)"
看命令执行完后,是否需要“手动输入东西”或者“进入另一个交互式环境”。
✅ 需要加 -t 的情况(交互式)
当你执行的命令会启动一个等待用户输入的程序,且你不希望 SSH 连接在程序启动后立即断开。
1. 启动交互式 Shell:
● 你只是想登录进去手动操作。
● ssh user@server
● ssh user@server "bash" (启动了一个新的 Shell,需要你输入命令)
2. 运行需要键盘输入的程序:
● ssh user@server "sudo some_command" (需要你输入密码)
● ssh user@server "vim /etc/config.txt" (需要你编辑文件)
● ssh user@server "python" (进入 Python 交互解释器)
3. 运行全屏程序:
● ssh user@server "top"
● ssh user@server "htop"
● ssh user@server "mysql -u root -p" (需要输入数据库密码)
如果不加 -t 会怎样?
● 命令可能会报错(例如 sudo: no tty present )。
● 或者程序一闪而过(比如 top 刚打开就退出了),因为程序发现没有终端可以显示画面。
❌ 不需要加 -t 的情况(非交互式/自动化)
当你执行的命令是“干完活就跑”的,不需要人盯着屏幕或者手动输入。
1. 执行单条命令:
● ssh user@server "ls /tmp"
● ssh user@server "df -h"
● ssh user@server "systemctl restart nginx"
2. 执行脚本/函数:
● 也就是你现在的场景,你的 remoteCMD 函数。
● ssh user@server "bash /path/to/script.sh"
3. 数据传输/查询:
● ssh user@server "cat /var/log/error.log"
● ssh user@server "ps aux | grep java"
如果误加了 -t 会怎样?
● 在单层 SSH 时:通常没太大副作用,只是多分配了一个不必要的终端资源。
● 在多层 SSH(跳板机)时:灾难性的。会导致连接卡在中间层,无法自动透传命令,必须手动 exit 才能退出。