通过SSH运行含后台进程的脚本:让SSH无需等待子进程退出
这个问题我之前也碰到过——核心原因是SSH会话会保持与远程shell的标准输入、输出、错误流的连接,哪怕你把进程放到后台,如果它还在使用这些流(比如你的echo "done"会输出到stdout),SSH就会一直等着这些流关闭才会断开连接返回。
下面给你几个实用的解决方案,按推荐程度排序:
1. 重定向后台进程的所有IO流
最简单直接的方式,把后台子进程的标准输出、标准错误都重定向到/dev/null(或者你指定的日志文件),让它彻底脱离SSH的会话流:
修改你的test.sh:
#!/bin/bash ( sleep 2 echo "done" ) > /dev/null 2>&1 &
这样后台进程的输出不会再占用SSH的会话通道,SSH执行完脚本后会立即返回,而后台的sleep和echo会继续运行。如果需要保存输出,把/dev/null换成具体的文件路径就行,比如./test.log。
2. 使用nohup启动守护进程
nohup是专门用来让进程脱离终端运行的工具,它会自动帮你处理IO重定向,还能让进程忽略挂起信号(比如SSH会话断开时的SIGHUP)。
可以直接在脚本里用:
#!/bin/bash nohup sh -c 'sleep 2; echo "done"' > /dev/null 2>&1 &
或者在SSH命令里直接调用:
ssh user@remote-host 'nohup ./test.sh > /dev/null 2>&1 &'
默认情况下nohup会把输出写到nohup.out,你也可以像上面那样自定义重定向。
3. 用disown移除后台作业
如果你不想修改IO流,可以在后台启动进程后,用bash的内置命令disown把它从shell的作业列表中移除——这样远程shell退出时,不会再跟踪这个后台进程,SSH也就会立即返回:
修改test.sh:
#!/bin/bash ( sleep 2 echo "done" ) & disown
不过这种方式要注意,如果后台进程还在使用SSH的会话流,可能还是会有延迟(比如echo "done"输出的时候,SSH可能会等输出完成),所以最好还是配合IO重定向一起用。
另外,如果你用的是ssh命令的-f参数(让SSH在后台运行),结合上面的方法效果会更好:
ssh -f user@remote-host './test.sh'
内容的提问来源于stack exchange,提问作者Mike

