当ssh管道断开时,停止python程序



我正在编写一个python脚本,其中包含一个在ssh上运行的无限while循环。我希望脚本在有人杀死ssh时终止。例如:

脚本(script.py):

while True:
# do something

将运行为:

ssh foo ./script.py

当我终止ssh进程时,我希望另一端的脚本停止运行。

我试着寻找一个封闭的stdout:

while not sys.stdout.closed:
# do something

但这并没有奏效。

我该如何做到这一点?

编辑

远程机器是一台Mac,它在csh:中打开程序

502 29352 ??         0:00.01 tcsh -c python test.py
502 29354 ??         0:00.04 python test.py

我从一个python脚本打开ssh进程,如下所示:

p = Popen(['ssh','foo','./script.py'],stdout=PIPE)
while True:
line = p.stdout.readline()
# etc

编辑

建议的解决方案:

  1. 使用while os.getppid() != 1运行脚本

这似乎在Linux系统上有效,但当远程计算机运行OSX时,不起作用。问题是该命令是在csh中启动的(见上文),因此csh的父进程id设置为1,但脚本没有。

  1. 定期登录到stderr

这是有效的,但脚本也在本地运行,我不想将心跳打印到stderr

  1. 使用ssh -tt在pseduo-tty中运行脚本

这确实有效,但会产生一些奇怪的后果。考虑以下内容:

remote_script:

#!/usr/bin/env python
import os
import time
import sys
while True:
print time.time()
sys.stdout.flush()
time.sleep(1)

local_script:

#!/usr/bin/env python
from subprocess import Popen, PIPE
import time
p = Popen(['ssh','-tt','user@foo','remote_script'],stdout=PIPE)
while True:
line = p.stdout.readline().strip()
if line:
print line
else:
break
time.sleep(10)

首先,输出真的很奇怪,它似乎一直在添加标签或其他东西:

[user@local ~]$ local_script 
1393608642.7
1393608643.71
1393608644.71
Connection to foo closed.

其次,程序在第一次收到SIGINT时不会退出,也就是说,我必须点击Ctrl-C两次才能杀死local_script。

好的,我有一个解决方案给你

当ssh连接关闭时,父进程id将从ssh deamon(处理连接的fork)的pid更改为1。

因此,以下内容解决了您的问题。

#!/usr/local/bin/python
from time import sleep
import os
#os.getppid() returns parent pid
while (os.getppid() != 1):
sleep(1)
pass

你能确认这对你来说也有效吗:)

编辑

我看到你更新了。

这还没有经过测试,但为了在OSX上实现这一想法,您可以检测csh的进程是否发生了变化。下面的代码仅说明了一个想法,尚未经过测试。也就是说,我认为它会起作用,但这不是最优雅的解决方案。如果能够找到使用signals的跨平台解决方案,则将是首选方案。

def do_stuff():
sleep(1)
if sys.platform == 'darwin':
tcsh_pid = os.getppid()
sshfork_pid = psutil.Process(tcsh_pid).ppid
while (sshfork_pid == psutil.Process(tcsh_pid).ppid)
do_stuff()
elif sys.platform == 'linux':
while (os.getppid() != 1):
sleep(1)
else:
raise Exception("platform not supported")
sys.exit(1)

你试过吗

ssh -tt foo ./script.py

当终端连接丢失时,应用程序应该接收SIGHUP信号,所以您所要做的就是使用信号模块注册一个特殊的处理程序。

import signal
def MyHandler(self, signum, stackFrame):
errorMessage = "I was stopped by %s" % signum
raise Exception(errorMessage)
# somewhere in the beginning of the __main__:
# registering the handler
signal.signal(signal.SIGHUP, MyHandler)

请注意,很可能您将不得不处理一些其他信号。你可以用完全相同的方式来做。

我建议定期登录到stderr。

当您不再有要写入的stderr时,这将导致发生异常。

运行的脚本是终端会话的子pid。如果您正确关闭SSH会话,它将终止进程。但是,实现这一点的另一种方法是将while循环连接到另一个因素,并将其与SSH会话断开连接。

您可以让cron控制您的脚本以定期执行。你可以让while循环有一个计数器。您可以在循环中使用sleep命令来控制执行。除了将它连接到SSH会话之外,几乎任何其他事情都是有效的。

要做到这一点,你可以使用exec&断开实例与循环的连接。

最新更新