当终止使用python子进程Popen启动的进程时,如何关闭输出管道?

时间:2022-08-01 07:32:27

I wonder if it is possible to shut down the communication pipe when killing a subprocess started in a different thread. If I do not call communicate() then kill() will work as expected, terminating the process after one second instead of five.

我想知道是否有可能在终止在不同线程中启动的子进程时关闭通信管道。如果我不调用communication(),那么kill()将按照预期工作,在一秒钟后终止进程,而不是在五秒之后。

I found a discussion of a similar problem here, but I got no real answers. I assume that I either have to be able to close the pipe or to explicitly kill the sub-subprocess (which is "sleep" in the example) and kill that to unblock the pipe.

我在这里发现了一个类似的问题,但我没有得到真正的答案。我假设我必须能够关闭管道,或者显式地终止子进程(在本例中是“sleep”),然后终止子进程以解除管道阻塞。

I also tried to find the answer her on SO, but I only found this and this and this, which do not directly address this problem as far as I can tell (?).

我也试着在SO上找到她的答案,但是我只找到了这个,这个,这个,这个,这个,这个,这个,我不能直接解决这个问题。

So the thing I want to do is to be able to run a command in a second thread and get all its output, but be able to kill it instantly when I so desire. I could go via a file and tail that or similar, but I think there should be a better way to do this?

因此,我想做的是能够在第二个线程中运行一个命令并得到它的所有输出,但是在我如此渴望的时候能够立即杀死它。我可以通过文件和跟踪,但我认为应该有更好的方法来做这个?

import subprocess, time
from threading import Thread

process = None

def executeCommand(command, runCommand):
    Thread(target=runCommand, args=(command,)).start()

def runCommand(command):
    global process
    args = command.strip().split()
    process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE)

    for line in process.communicate():
        if line:
            print "process:", line,

if __name__ == '__main__':
    executeCommand("./ascript.sh", runCommand)
    time.sleep(1)
    process.kill()

This is the script:

这是脚本:

#!/bin/bash
echo "sleeping five"
sleep 5
echo "slept five"

Output

输出

$ time python poc.py 
process: sleeping five

real    0m5.053s
user    0m0.044s
sys 0m0.000s

3 个解决方案

#1


6  

I think the problem is that process.kill() only kills the immediate child process (bash), not the sub-processes of the bash script.

我认为问题在于process.kill()只杀死直接子进程(bash),而不是bash脚本的子进程。

The problem and solution are described here:

问题和解决办法如下:

Use Popen(..., preexec_fn=os.setsid) to create a process group and os.pgkill to kill the entire process group. eg

使用Popen(…创建进程组和操作系统。杀死整个进程组。如

import os
import signal
import subprocess
import time
from threading import Thread

process = None

def executeCommand(command, runCommand):
    Thread(target=runCommand, args=(command,)).start()

def runCommand(command):
    global process
    args = command.strip().split()
    process = subprocess.Popen(
        args, shell=False, stdout=subprocess.PIPE, preexec_fn=os.setsid)

    for line in process.communicate():
        if line:
            print "process:", line,

if __name__ == '__main__':
    executeCommand("./ascript.sh", runCommand)
    time.sleep(1)
    os.killpg(process.pid, signal.SIGKILL)

$ time python poc.py 
process: sleeping five

real    0m1.051s
user    0m0.032s
sys 0m0.020s

#2


0  

It seems to me that the easiest way to do this and sidestep the multithreading problems would be to set a kill flag from the main thread, and check for it in the script-running thread just before the communication, killing the script when the flag is True.

在我看来,实现这一点并避开多线程问题的最简单方法是从主线程中设置一个kill标志,并在通信之前在脚本运行的线程中检查它,在标志为真时杀死脚本。

#3


-1  

It looks like you may be a victim of Python's super coarse grained concurrency. Change your script to this:

看起来您可能是Python超粗粒度并发的受害者。把你的剧本改成这样:

#!/bin/bash
echo "sleeping five"
sleep 5
echo "sleeping five again"
sleep 5
echo "slept five"

And then the output becomes:

然后输出变成:

process: sleeping five

real    0m5.134s
user    0m0.000s
sys     0m0.010s

If the entire script ran, the time would be 10s. So it looks like the python control thread doesn't actually run until after the bash script sleeps. Similarly, if you change your script to this:

如果整个脚本运行,时间将是10秒。所以看起来python控制线程直到bash脚本休眠之后才运行。类似地,如果您将脚本更改为以下内容:

#!/bin/bash
echo "sleeping five"
sleep 1
sleep 1
sleep 1
sleep 1
sleep 1
echo "slept five"

Then the output becomes:

然后输出就变成:

process: sleeping five

real    0m1.150s
user    0m0.010s
sys     0m0.020s

In short, your code works as logically implemented. :)

简而言之,您的代码按照逻辑实现工作。:)

#1


6  

I think the problem is that process.kill() only kills the immediate child process (bash), not the sub-processes of the bash script.

我认为问题在于process.kill()只杀死直接子进程(bash),而不是bash脚本的子进程。

The problem and solution are described here:

问题和解决办法如下:

Use Popen(..., preexec_fn=os.setsid) to create a process group and os.pgkill to kill the entire process group. eg

使用Popen(…创建进程组和操作系统。杀死整个进程组。如

import os
import signal
import subprocess
import time
from threading import Thread

process = None

def executeCommand(command, runCommand):
    Thread(target=runCommand, args=(command,)).start()

def runCommand(command):
    global process
    args = command.strip().split()
    process = subprocess.Popen(
        args, shell=False, stdout=subprocess.PIPE, preexec_fn=os.setsid)

    for line in process.communicate():
        if line:
            print "process:", line,

if __name__ == '__main__':
    executeCommand("./ascript.sh", runCommand)
    time.sleep(1)
    os.killpg(process.pid, signal.SIGKILL)

$ time python poc.py 
process: sleeping five

real    0m1.051s
user    0m0.032s
sys 0m0.020s

#2


0  

It seems to me that the easiest way to do this and sidestep the multithreading problems would be to set a kill flag from the main thread, and check for it in the script-running thread just before the communication, killing the script when the flag is True.

在我看来,实现这一点并避开多线程问题的最简单方法是从主线程中设置一个kill标志,并在通信之前在脚本运行的线程中检查它,在标志为真时杀死脚本。

#3


-1  

It looks like you may be a victim of Python's super coarse grained concurrency. Change your script to this:

看起来您可能是Python超粗粒度并发的受害者。把你的剧本改成这样:

#!/bin/bash
echo "sleeping five"
sleep 5
echo "sleeping five again"
sleep 5
echo "slept five"

And then the output becomes:

然后输出变成:

process: sleeping five

real    0m5.134s
user    0m0.000s
sys     0m0.010s

If the entire script ran, the time would be 10s. So it looks like the python control thread doesn't actually run until after the bash script sleeps. Similarly, if you change your script to this:

如果整个脚本运行,时间将是10秒。所以看起来python控制线程直到bash脚本休眠之后才运行。类似地,如果您将脚本更改为以下内容:

#!/bin/bash
echo "sleeping five"
sleep 1
sleep 1
sleep 1
sleep 1
sleep 1
echo "slept five"

Then the output becomes:

然后输出就变成:

process: sleeping five

real    0m1.150s
user    0m0.010s
sys     0m0.020s

In short, your code works as logically implemented. :)

简而言之,您的代码按照逻辑实现工作。:)