Domanda

Devo eseguire un comando shell in modo asincrono da uno script Python. Con questo intendo che voglio che il mio script Python continui a funzionare mentre il comando esterno si spegne e fa tutto ciò che deve fare.

Ho letto questo post:

  

Chiamare un comando esterno in Python

Poi sono andato fuori e ho fatto alcuni test, e sembra che os.system () farà il lavoro a condizione che io usi & amp; alla fine del comando in modo che non debba aspettare che ritorni. Quello che mi chiedo è se questo è il modo corretto di realizzare una cosa del genere? Ho provato command.call () ma non funzionerà per me perché si blocca sul comando esterno.

Per favore fatemi sapere se è consigliabile utilizzare os.system () per questo o se dovrei provare qualche altro percorso.

È stato utile?

Soluzione

subprocess.Popen fa esattamente quello che vuoi.

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

(Modifica per completare la risposta dai commenti)

L'istanza Popen può fare varie altre cose come te poll () per vedere se è ancora in esecuzione e puoi communic () con esso per inviare i dati su stdin e attendere che termini.

Altri suggerimenti

Se vuoi eseguire molti processi in parallelo e poi gestirli quando producono risultati, puoi usare il polling come nel seguente:

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)

Il flusso di controllo è un po 'contorto perché sto cercando di renderlo piccolo: puoi fare il refactoring secondo i tuoi gusti. : -)

Questo ha il vantaggio di soddisfare prima le richieste di rifinitura anticipata. Se chiami comunica sul primo processo in esecuzione e risulta essere il più lungo, l'altro i processi in esecuzione saranno rimasti lì inattivi quando avresti potuto gestirne i risultati.

Quello che mi chiedo è se questo [os.system ()] è il modo corretto di realizzare una cosa del genere?

No. os.system () non è il modo corretto. Ecco perché tutti dicono di usare sottoprocesso .

Per ulteriori informazioni, leggi http://docs.python.org/library /os.html#os.system

  

Il modulo di sottoprocesso fornisce di più   potenti strutture per la generazione di nuovi   processi e recupero dei loro   risultati; usando quel modulo è   preferibile utilizzare questa funzione. Uso   il modulo di sottoprocesso. Dai un'occhiata   in particolare il Vecchio sostituto   Funziona con il modulo sottoprocesso   sezione.

Ho avuto un buon successo con il asyncproc , che si occupa perfettamente dell'output dei processi. Ad esempio:

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

Utilizzo di pexpect [ http://www.noah.org/wiki/Pexpect ] con readline non bloccanti è un altro modo per farlo. Pexpect risolve i problemi di deadlock, consente di eseguire facilmente i processi in background e offre dei modi semplici per avere richiamate quando il processo sputa stringhe predefinite e in generale rende molto più semplice l'interazione con il processo.

Ho lo stesso problema nel tentativo di connettermi a un terminale 3270 usando il software di scripting s3270 in Python. Ora sto risolvendo il problema con una sottoclasse di Process che ho trovato qui:

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

Ed ecco l'esempio preso dal file:

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 " Non devo aspettare che ritorni " una delle soluzioni più semplici sarà questa:

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

Ma ... Da quello che ho letto questo non è "il modo corretto di realizzare una cosa del genere" a causa dei rischi per la sicurezza creati dal flag subprocess.CREATE_NEW_CONSOLE .

Le cose chiave che accadono qui sono l'uso di subprocess.CREATE_NEW_CONSOLE per creare una nuova console e .pid (restituisce l'ID del processo in modo da poter controllare il programma in seguito se voglio) in modo da non aspettare che il programma finisca il suo lavoro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top