Paramiko 和 exec_command - 杀死远程进程?

发布于 2024-12-09 05:54:57 字数 1193 浏览 1 评论 0原文

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

以前,我们通过 ssh -t 运行此命令,但事实证明这很不稳定,而且 -t 导致我们的远程调度系统出现问题。

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

我的脚本(基于长-在 python paramiko 模块中运行 ssh 命令(以及如何结束它们)

#!/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。

I'm using Paramiko to tail -f a file on a remote server.

Previously, we were running this via ssh -t, but that proved flaky, and the -t caused issues with our remote scheduling system.

My question is how to kill tail when the script catches a SIGINT?

My script (based on 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)

The script catches my Ctrl-C successfully, and ends. However, it leaves the tail -f process running on the remote system,.

Neither client.close() nor channel.close() seem to terminate it.

What command can I issue in the except block to kill it?

The remote server is running Solaris 10.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

满地尘埃落定 2024-12-16 05:54:57

您应该使用 ssh keepalives...您遇到的问题是远程 shell 无法知道(默认情况下)您的 ssh 会话已被终止。 Keepalives 将使远程 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 登录已终止,并将终止任何会话由它产生的进程。

You should use ssh keepalives... the problem you have is that the remote shell has no way of knowing (by default) that your ssh session was killed. Keepalives will enable the remote shell to detect that you killed the session

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...

Set the keepalive value as low as you like (even 1 second)... after several seconds, the remote shell will see that the ssh login died, and it will terminate any processes that were spawned by it.

梦太阳 2024-12-16 05:54:57

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

ssh -t commandname

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

channel.get_pty()

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

There is one way to do this. It works like on the shell

ssh -t commandname

The option -t is opening a pseudo pty to help ssh to track how long this process should last. the same can be done via pormiko via

channel.get_pty()

prior to execute_command(...). This will not open a shell like it does with channel.invoke_shell(), it just requests such a pseudo interface to tie all processes to. The effect can also be seen if ps aux is issued on the remote machine, the process is now attached to the sshd with a ptxXY interface.

才能让你更想念 2024-12-16 05:54:57

我刚刚遇到这个问题,但无法最终发出 pkill 来关闭该进程。

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

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

这将允许您根据需要运行 tail 命令。一旦您向远程进程发送换行符,kill %1 就会执行并停止您后台运行的 tail 命令。 (供参考:%1 是一个作业规范,用于描述会话中第一个后台进程,即 tail 命令)

I just hit this issue and wasn't in a position to issue a pkill to close the process at the end.

A better solution is to change the command you are running to:

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

This will let you run your tail command for as long as you need. As soon as you send a newline to the remote process the kill %1 will execute and stop the tail command you backgrounded. (for reference: %1 is a jobspec and used to describe the first process that has been backgrounded in your session, ie the tail command)

垂暮老矣 2024-12-16 05:54:57

以下是获取远程进程 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)
        # ...

Here's a way to obtain the remote process 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

And here's how to use it (replace ... with the bits in the original question):

pid, _, _, _ = execute(channel, "tail -f /home/victorhooi/macbeth.txt")
while True:
    try:
        # ...
    except KeyboardInterrupt:
        client.exec_command("kill %d" % pid)
        # ...
自在安然 2024-12-16 05:54:57

虽然不是最有效的方法,但这应该可行。 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 a ps,得到pid 并 kill -9 它。

首先,将 tail 设置为在跟随之前从文件末尾读取 n 行。将 n 设置为一个唯一的数字,例如 time.time(),因为 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))

While not the most efficient method, this should work. After you CTRL+C; In the KeyboardInterrupt handler you could exec_command("killall -u %s tail" % uname) like so:

#!/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)

This would kill any open processes named tail. That may cause issues though if you have tails open that you dont want to close, if thats the case you could grep a ps, get the pid and kill -9 it.

First, set tail to read n lines from end of file before following. set n to a unique nuber like time.time(), since tail doesn't care if that number is larger then the number of lines in the file, the large number from time.time()shouldnt cause issues and will be unique. Then grep for that unique number in the 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))
无尽的现实 2024-12-16 05:54:57

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

Had the same problem with ssh -t. There is a library called closer - it runs a remote process via ssh and closes automatically for you. Check it out.

终弃我 2024-12-16 05:54:57

特别是对于“tail”,您可以使用 --pid=PID 参数并让 tail 处理它:

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

Specifically for 'tail' you could use the --pid=PID argument and let tail take care of it:

  --pid=PID  with -f, terminate after process ID, PID dies
素年丶 2024-12-16 05:54:57

您可以按照 https://stackoverflow.com/a/38883662/565212 中的说明使用 get_pty。

例如场景 - 何时调用 client/channel.close():

第 1 步:执行写入日志文件的远程命令。

第2步:生成一个执行tail命令并阻塞在readline循环中的线程

第三步:在主线程中,当命令返回时,您知道不会有更多日志,杀死尾线程。

You can use get_pty as described in https://stackoverflow.com/a/38883662/565212.

E.g. scenario - When to call client/channel.close():

Step1: Execute a remote command that writes to the log file.

Step2: Spawn a thread that executes the tail command and blocks in the readline loop

Step3: In main thread, when the command returns, you know there will be no more logs, kill the tail thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文