Como faço para fechar o stdout-pipe quando matar um processo iniciado com python subprocess Popen?

StackOverflow https://stackoverflow.com/questions/1624254

Pergunta

Gostaria de saber se é possível desligar o tubo de comunicação quando matar um subprocesso iniciado em um segmento diferente. Se eu não chamar comunicar (), em seguida, matar () irá funcionar como esperado, que encerra o processo após um segundo em vez de cinco.

Eu encontrei uma discussão sobre um problema semelhante aqui , mas eu não tenho respostas reais. Eu suponho que eu quer ter de ser capaz de fechar o tubo ou para explicitamente matar o sub-sub-processo (que é "dormir" no exemplo) e matar que para desbloquear o tubo.

Eu também tentei encontrar a resposta dela no SO, mas eu só encontrei este e este e this , que não tratam diretamente este problema, tanto quanto eu posso dizer ( ?).

Assim, a coisa que eu quero fazer é ser capaz de executar um comando em um segundo thread e obter toda a sua saída, mas ser capaz de matá-lo instantaneamente quando eu assim o desejarem. Eu poderia ir através de um arquivo e cauda que ou similar, mas eu acho que deveria haver uma maneira melhor de fazer isso?

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()

Este é o script:

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

saída

$ time python poc.py 
process: sleeping five

real    0m5.053s
user    0m0.044s
sys 0m0.000s
Foi útil?

Solução

Eu acho que o problema é que process.kill () só mata o processo filho imediato (bash), não os sub-processos do script bash.

O problema ea solução são descritos aqui:

Use Popen (..., preexec_fn = os.setsid) para criar um grupo de processos e os.pgkill para matar todo o grupo processo. eg

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

Outras dicas

Parece-me que a maneira mais fácil de fazer isso e contornar os problemas multithreading seria para definir um sinal kill do thread principal, e verificar se há-lo no segmento de execução de script pouco antes da comunicação, matando o script quando a bandeira é True.

Parece que você pode ser uma vítima de super-grossa concorrência grained do Python. Mude o seu script para o seguinte:

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

E, em seguida, a saída torna-se:

process: sleeping five

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

Se todo o script foi executado, o tempo seria 10s. Portanto, parece que o segmento de controle de python na verdade não executar até depois das dorme de script bash. Da mesma forma, se você mudar de script para isso:

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

Então, a saída torna-se:

process: sleeping five

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

Em suma, o código funciona como logicamente implementados. :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top