为何花括号包裹命令时SSH远程执行无法转入后台运行
问题原因分析
核心原理:SSH 执行远端命令时,只有当远端所有进程都关闭了和当前 SSH 会话绑定的**标准输入(stdin)、标准输出(stdout)、标准错误(stderr)**三个文件描述符后,SSH 才会断开连接退出。
首先明确 POSIX 标准定义的两种复合命令的本质差异:
( 命令 ):属于子 shell 复合命令,会启动独立的子 shell 进程执行括号内的命令,重定向操作作用在子 shell 进程上,子 shell 退出后,SSH 启动的顶层登录 shell 不会继承子 shell 内后台进程的文件描述符关联。{ 命令; }:属于当前 shell 组命令,不会启动新的子进程,所有命令直接在当前登录 shell 上下文中执行,重定向操作直接作用在当前 shell 进程上。
结合你的测试场景分析:
前三个测试用例中,后台执行的进程要么直接将所有输出重定向到/dev/null,要么在独立子进程中运行,所有和 SSH 会话绑定的文件描述符都被正确解绑,因此 SSH 可以正常立即退出。
使用{}包裹命令触发卡住的核心原因和 QNX 7 的默认 shell 实现有关:
POSIX 标准没有强制要求 shell 为后台运行的进程自动重定向标准输入,常用的 bash 做了额外的易用性优化,会自动将后台进程的 stdin 重定向到/dev/null,所以在 bash 环境下测试{}后台执行的场景不会卡住;但 QNX 7 的默认 sh 严格遵循 POSIX 规范,没有做这个兼容处理,当你把组命令放到后台运行时,后台进程仍然持有 SSH 会话的 stdin 文件描述符,导致 SSH 判定还有进程在使用会话连接,因此会一直等待到 sleep 执行完成才会退出。
解决方案
如果要在 QNX 的 sh 环境下使用{}组命令后台执行且不卡住 SSH,只需手动将 stdin 也重定向到/dev/null即可,示例命令如下:
time ssh $IP '{ sleep 20; } >/dev/null 2>&1 < /dev/null &'
修改后三个文件描述符都和 SSH 会话完全解绑,SSH 就会正常立即退出。
内容的提问来源于stack exchange,提问作者ThomasN
相关产品推荐
相关产品推荐

