Paramiko和exec_command - 杀死远程进程?

时间:2011-10-12 02:40:54

标签: python ssh solaris signals paramiko

我正在使用Paramiko来tail -f远程服务器上的文件。

以前,我们是通过ssh -t运行的,但事实证明这种情况不稳定,-t导致我们的远程调度系统出现问题。

我的问题是当脚本捕获SIGINT时如何杀死尾部?

我的脚本(基于Long-running ssh commands in python paramiko module (and how to end them)

#!/usr/bin/env python2
import paramiko
import select

client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
channel = transport.open_session()

channel.exec_command("tail -f /home/victorhooi/macbeth.txt")
while True:
    try:
        rl, wl, xl = select.select([channel],[],[],0.0)
        if len(rl) > 0:
            # Must be stdout
            print channel.recv(1024)
    except KeyboardInterrupt:
        print("Caught control-C")
        client.close()
        channel.close()
        exit(0)

脚本成功捕获 Ctrl-C ,然后结束。但是,它使tail -f进程在远程系统上运行,。

client.close()和channel.close()似乎都没有终止它。

我可以在except块中发出什么命令来杀死它?

远程服务器正在运行Solaris 10.

8 个答案:

答案 0 :(得分:6)

有一种方法可以做到这一点。它就像在shell上一样工作

ssh -t commandname

选项-t正在打开一个伪pty来帮助ssh跟踪这个过程应该持续多长时间。同样可以通过pormiko来完成

channel.get_pty()

在execute_command(...)之前。这不会像使用channel.invoke_shell()一样打开shell,它只是请求这样的伪接口将所有进程绑定到。如果在远程计算机上发出ps aux,也可以看到效果,此过程现在通过ptxXY接口附加到sshd。

答案 1 :(得分:5)

你应该使用ssh keepalive ...你遇到的问题是远程shell无法知道(默认情况下)你的ssh会话被杀死了。 Keepalive将使远程shell能够检测到你杀死了会话

client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
transport.set_keepalive(1)   # <------------------------------
# ... carry on as usual...

将keepalive值设置为您想要的最低值(甚至1秒)......几秒钟后,远程shell将看到ssh登录名死亡,它将终止由它生成的任何进程。

答案 2 :(得分:4)

我刚刚遇到这个问题,并且无法在最后发出关闭该流程的信息。

更好的解决方案是将您正在运行的命令更改为:

tail -f /path/to/file & { read ; kill %1; }

这将允许您根据需要运行tail命令。只要您向远程进程发送换行符,kill%1就会执行并停止后退命令。 (供参考:%1是一个jobspec,用于描述在会话中已经背景化的第一个进程,即tail命令)

答案 3 :(得分:1)

以下是获取远程进程ID的方法:

def execute(channel, command):
    command = 'echo $$; exec ' + command
    stdin, stdout, stderr = channel.exec_command(command)
    pid = int(stdout.readline())
    return pid, stdin, stdout, stderr

以下是如何使用它(将...替换为原始问题中的位):

pid, _, _, _ = execute(channel, "tail -f /home/victorhooi/macbeth.txt")
while True:
    try:
        # ...
    except KeyboardInterrupt:
        client.exec_command("kill %d" % pid)
        # ...

答案 4 :(得分:0)

虽然不是最有效的方法,但这应该有效。在 CTRL + C 之后;在KeyboardInterrupt处理程序中,你可以exec_command("killall -u %s tail" % uname)这样:

#!/usr/bin/env python2

import paramiko
import select

import time
ltime = time.time()

# Or use random:
# import random
# ltime = random.randint(0, 500)

uname = "victorhooi"
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username=uname, password='blahblah')
transport = client.get_transport()
channel = transport.open_session()

channel.exec_command("tail -%df /home/victorhooi/macbeth.txt" % ltime)
while True:
    try:
        rl, wl, xl = select.select([channel],[],[],0.0)
        if len(rl) > 0:
            # Must be stdout
            print channel.recv(1024)
    except KeyboardInterrupt:
        print("Caught control-C")
        channel.close()
        try:
            # open new socket and kill the proc..
            client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))
        except:
            pass
    
        client.close()
        exit(0)

这会杀死任何名为tail的打开进程。这可能会导致问题,但是如果你tail打开了你不想关闭的话,那么你可以grep ps,获得pid和kill -9

首先,在跟随之前将tail设置为从文件末尾读取n行。将n设置为像time.time()这样的唯一nuber,因为tail不关心该数字是否大于文件中的行数,time.time()中的大数字不应导致问题,将是独一无二的。然后grep查找ps

中的唯一编号
   client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))

答案 5 :(得分:0)

特别是对于'tail',你可以使用--pid = PID参数并让tail来处理它:

  --pid=PID  with -f, terminate after process ID, PID dies

答案 6 :(得分:0)

您可以按https://stackoverflow.com/a/38883662/565212

中所述使用get_pty

E.g。 scenario - 何时调用client / channel.close():
步骤1:执行写入日志文件的远程命令。
步骤2:产生执行tail命令的线程并在readline循环中阻塞
第三步:在主线程中,当命令返回时,你知道没有更多的日志,杀死尾线。

答案 7 :(得分:0)

ssh -t有同样的问题。有一个名为closer的库 - 它通过ssh运行远程进程并自动关闭。看看吧。