当ssh管道坏了时停止python程序

时间:2014-02-12 14:50:59

标签: python linux ssh

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

脚本(script.py):

while True:
    # do something

将以:

运行
ssh foo ./script.py

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

我试过寻找一个封闭的标准输出:

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
  2. 运行脚本

    这似乎适用于Linux系统,但在远程机器运行OSX时工作。问题是该命令是在csh中启动的(见上文),因此csh的父进程id设置为1,而不是脚本。

    1. 定期登录stderr
    2. 这样可行,但脚本也在本地运行,我不想将心跳打印到stderr

      1. 使用ssh -tt

        这确实有效,但有一些奇怪的后果。请考虑以下事项:

        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。

5 个答案:

答案 0 :(得分:6)

好的,我有一个解决方案

当ssh连接关闭时,父进程id将从ssh-deamon的pid(处理连接的fork)更改为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)

答案 1 :(得分:3)

你试过吗

ssh -tt foo ./script.py

答案 2 :(得分:3)

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

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)

请注意,您很可能必须处理其他一些信号。你可以用同样的方式做到这一点。

答案 3 :(得分:2)

我建议定期登录stderr。

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

答案 4 :(得分:0)

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

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

要做到这一点,你可以使用exec&从循环中断开实例。