Pergunta

Eu preciso executar um comando shell de forma assíncrona a partir de um script Python. Com isto quero dizer que eu quero o meu script Python para continuar em execução enquanto o comando externo sai e faz tudo o que ele precisa fazer.

Eu li este post:

Chamando um comando externo em Python

Eu, então, saiu e fez alguns testes, e parece que os.system() irá fazer o trabalho, desde que eu uso & no final do comando para que eu não tenho que esperar por ele para voltar. O que eu estou querendo saber é se esta é a maneira correta de realizar tal coisa um? Tentei commands.call() mas não vai funcionar para mim, porque ele bloqueia no comando externo.

Por favor, deixe-me saber se usando os.system() para isso é aconselhável ou se eu deveria tentar alguma outra rota.

Foi útil?

Solução

subprocess.Popen faz exatamente o que você quer.

from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()

(Edit para completar a resposta de Comentários)

A instância Popen pode fazer várias outras coisas como você pode poll() -lo para ver se ele ainda está em execução, e você pode communicate() com ele para enviá-lo dados sobre stdin, e esperar por ele para terminar.

Outras dicas

Se você deseja executar muitos processos em paralelo e, em seguida, tratá-los quando eles produzir resultados, você pode usar pesquisa como no seguinte:

from subprocess import Popen, PIPE
import time

running_procs = [
    Popen(['/usr/bin/my_cmd', '-i %s' % path], stdout=PIPE, stderr=PIPE)
    for path in '/tmp/file0 /tmp/file1 /tmp/file2'.split()]

while running_procs:
    for proc in running_procs:
        retcode = proc.poll()
        if retcode is not None: # Process finished.
            running_procs.remove(proc)
            break
        else: # No process is done, wait a bit and check again.
            time.sleep(.1)
            continue

    # Here, `proc` has finished with return code `retcode`
    if retcode != 0:
        """Error handling."""
    handle_results(proc.stdout)

O fluxo de controle há um pouco complicado, porque eu estou tentando fazê-lo pequeno - você pode refatorar ao seu gosto. : -)

Isto tem a vantagem de atender os pedidos de início de terminar em primeiro. Se você chamar communicate no primeiro processo em execução e que acaba por ser executado o mais longo, os outros processos em execução terá sido sentado lá ocioso quando você poderia ter sido manipulação de seus resultados.

O que eu estou querendo saber é se isso [os.system ()] é a maneira correta de realizar tal coisa?

No. os.system() não é a maneira correta. É por isso que toda a gente diz para uso subprocess.

Para obter mais informações, leia http://docs.python.org/library /os.html#os.system

O módulo subprocesso fornece mais instalações poderosas para desova nova processos e recuperar sua resultados; usando esse módulo é preferível utilizar esta função. Usar o módulo subprocesso. Verifica especialmente os mais velhos Substituir Funções com o módulo de subprocesso seção.

Eu tive um bom sucesso com o asyncproc módulo , que lida bem com a saída dos processos. Por exemplo:

import os
from asynproc import Process
myProc = Process("myprogram.app")

while True:
    # check to see if process has ended
    poll = myProc.wait(os.WNOHANG)
    if poll is not None:
        break
    # print any new output
    out = myProc.read()
    if out != "":
        print out

Usando pexpect [ http://www.noah.org/wiki/Pexpect ] com readlines non-blocking é outra maneira de fazer isso. Pexpect resolve os problemas de impasse, permite-lhe executar facilmente os processos em segundo plano, e dá maneiras fáceis de ter retornos de chamada quando seus espetos de processo fora cordas predefinidos e geralmente faz a interação com o processo muito mais fácil.

Eu tenho o mesmo problema ao tentar conectar a um terminal 3270 usando o software s3270 script em Python. Agora eu estou resolvendo o problema com uma subclasse de Processo que eu encontrei aqui:

http://code.activestate.com/recipes/440554/

E aqui está a amostra retirada do arquivo:

def recv_some(p, t=.1, e=1, tr=5, stderr=0):
    if tr < 1:
        tr = 1
    x = time.time()+t
    y = []
    r = ''
    pr = p.recv
    if stderr:
        pr = p.recv_err
    while time.time() < x or r:
        r = pr()
        if r is None:
            if e:
                raise Exception(message)
            else:
                break
        elif r:
            y.append(r)
        else:
            time.sleep(max((x-time.time())/tr, 0))
    return ''.join(y)

def send_all(p, data):
    while len(data):
        sent = p.send(data)
        if sent is None:
            raise Exception(message)
        data = buffer(data, sent)

if __name__ == '__main__':
    if sys.platform == 'win32':
        shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')
    else:
        shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n')

    a = Popen(shell, stdin=PIPE, stdout=PIPE)
    print recv_some(a),
    for cmd in commands:
        send_all(a, cmd + tail)
        print recv_some(a),
    send_all(a, 'exit' + tail)
    print recv_some(a, e=0)
    a.wait()

Considerando "Eu não tenho que esperar por ele para retornar", uma das soluções mais fáceis será este:

subprocess.Popen( \
    [path_to_executable, arg1, arg2, ... argN],
    creationflags = subprocess.CREATE_NEW_CONSOLE,
).pid

Mas ... Pelo que li isso não é "a maneira correta de realizar tal coisa" por causa de riscos de segurança criado pela bandeira subprocess.CREATE_NEW_CONSOLE.

As principais coisas que acontecem aqui é o uso de subprocess.CREATE_NEW_CONSOLE para criar novo console e .pid (retornos processar ID de modo que você pode verificar programa mais tarde, se você quiser) para que não esperar por programa para terminar o seu trabalho.

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