通过subprocess.Popen和ssh向远程进程发送Ctrl-C
我该如何给多个 ssh -t
进程发送 Ctrl-C
呢?
我有一些Python代码,可以在远程主机上启动一个脚本:
# kickoff.py
# i call 'ssh' w/ the '-t' flag so that when i press 'ctrl-c', it get's
# sent to the script on the remote host. otherwise 'ctrol-c' would just
# kill things on this end, and the script would still be running on the
# remote server
a = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'a'])
a.communicate()
这个方法很好用,但我需要在远程主机上同时启动多个脚本:
# kickoff.py
a = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'a'])
b = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'b'])
a.communicate()
b.communicate()
结果是,按 Ctrl-C
并不能可靠地终止所有进程,而且我的终端在之后总是会出现乱码(我得运行 'reset' 来恢复)。那么,如何在主脚本被终止时同时杀掉两个远程脚本呢?
注意:我想避免登录到远程主机,查找进程列表中的 'script.sh',然后给两个进程发送 SIGINT 信号。我只想在启动脚本时按 Ctrl-C
,就能同时终止两个远程进程。一个不太理想的解决方案可能是确定性地找到远程脚本的进程ID,但我不知道在我现在的设置中该怎么做。
更新:在远程服务器上启动的脚本实际上会启动几个子进程,虽然杀掉 ssh
确实能终止原始的远程脚本(可能是因为 SIGHUP 信号),但子任务并不会被终止。
4 个回答
我还没试过这个,不过你可以试着捕捉一个键盘中断,然后结束那些进程:
try
a = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'a'])
b = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'b'])
a.communicate()
b.communicate()
except KeyboardInterrupt:
os.kill(a.pid, signal.SIGTERM)
os.kill(b.pid, signal.SIGTERM)
当你结束ssh连接时,它会向远程的进程发送一个叫做SIGHUP的信号。你可以把这些远程进程放在一个shell脚本或者python脚本里,这样当这个脚本收到SIGHUP信号时,就会把它们杀掉(在bash中可以用trap命令,python里有signal模块)。
其实,你也可以用一个复杂的命令行来实现,而不一定非要用远程的包装脚本。
问题在于,杀掉远程进程并不是你真正想要的。你真正想要的是在按下Ctrl+C后,终端还能正常工作。为了做到这一点,你需要同时杀掉远程进程,并且看到剩下的输出,这些输出里会有一些终端控制序列,用来把终端恢复到正常状态。为此,你需要一种机制来通知包装脚本去杀掉这些进程。这两者是不同的事情。
我唯一能成功结束所有子进程的方法是使用pexpect这个工具:
a = pexpect.spawn(['ssh', 'remote-host', './script.sh', 'a'])
a.expect('something')
b = pexpect.spawn(['ssh', 'remote-host', './script.sh', 'b'])
b.expect('something else')
# ...
# to kill ALL of the children
a.sendcontrol('c')
a.close()
b.sendcontrol('c')
b.close()
这个方法挺可靠的。我记得之前有人也发过这个答案,但后来把它删掉了,所以我就把它发出来,以防其他人也想知道。