SSH远程运行Jupyter时Ctrl-C无法传递至进程的问题排查与解决
我想创建一个别名来在远程机器上快速运行Jupyter,写了以下命令:
ssh -L 5542:localhost:5542 remote \ 'cd ipython && .direnv/python-3.8.10/bin/jupyter notebook --no-browser --port 5542'
命令能正常运行,但有个问题:终端里按Ctrl-C无法传递给jupyter-notebook进程(它需要按两次Ctrl-C才能干净关闭),反而会终止shell进程并关闭连接,导致Jupyter进程变成孤儿,无法正常关闭。
我尝试用shell脚本封装命令(但更希望直接把整个命令作为SSH参数传递),甚至尝试捕获SIGINT信号:
#!/bin/bash cd "$HOME/ipython" foo() { echo break } trap foo SIGINT .direnv/python-3.8.10/bin/jupyter notebook --no-browser --port 5542
但执行ssh remote run-jupyter.sh后问题依旧:只显示^C,连接终止,Jupyter进程变成孤儿,也没输出break。是不是Ctrl-C直接关闭了我的SSH客户端?
请问这是什么原因?怎么修复才能让Ctrl-C正确传递给Jupyter进程?
(注:我知道可以通过Web客户端菜单关闭Jupyter,这能正常工作,但不是我想要的解决方式。)
环境信息:
localhost:Mac OS Ventura 13.2.1,GNU bash 5.2.15(1)-release (x86_64-apple-darwin22.1.0),终端为Terminal 2.13 (447)remote:Ubuntu 20.04.6 LTS,GNU bash 5.0.17(1)-release (x86_64-pc-linux-gnu)
原因分析
默认情况下,SSH执行远程命令时不会分配伪终端(pseudo-TTY),此时SIGINT信号(Ctrl-C触发)会直接发送给本地SSH客户端,而非远程的Jupyter进程。SSH客户端终止后,远程Jupyter进程失去父进程,变成孤儿进程被init进程接管,无法被正常关闭。
解决方案
1. 给SSH添加-t参数强制分配伪终端
这是最直接的解决方法,-t参数会让SSH在远程端创建一个伪终端,信号可以通过终端正常传递:
ssh -t -L 5542:localhost:5542 remote \ 'cd ipython && .direnv/python-3.8.10/bin/jupyter notebook --no-browser --port 5542'
此时按一次Ctrl-C就能正常关闭Jupyter,同时终止SSH连接。
2. 脚本封装时配合-t参数
如果使用脚本执行,同样需要在SSH命令里加-t:
ssh -t remote ./run-jupyter.sh
脚本可以简化,无需额外的信号捕获逻辑:
#!/bin/bash cd "$HOME/ipython" .direnv/python-3.8.10/bin/jupyter notebook --no-browser --port 5542
3. 创建别名简化日常使用
把带-t的命令添加到本地shell配置文件(比如~/.bashrc或~/.zshrc),设置为别名:
alias jupyter-remote='ssh -t -L 5542:localhost:5542 remote "cd ipython && .direnv/python-3.8.10/bin/jupyter notebook --no-browser --port 5542"'
保存配置后执行source ~/.bashrc(对应你的shell配置文件),之后直接输入jupyter-remote就能启动远程Jupyter,且Ctrl-C可以正常关闭进程。
内容的提问来源于stack exchange,提问作者Amadan

