2015-11-19 23 views
5

Attualmente sto scrivendo il mio primo programma python (in Python 2.6.6). Il programma facilita l'avvio e l'arresto di diverse applicazioni in esecuzione su un server fornendo all'utente comandi comuni (come l'avvio e l'arresto di servizi di sistema su un server Linux).Come posso avviare un processo e metterlo in background in python?

Sto iniziando script di avvio delle applicazioni da

p = subprocess.Popen(startCommand, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
output, err = p.communicate() 
print(output) 

Il problema è che lo script di avvio di un'applicazione rimane in primo piano e in modo p.communicate() attende per sempre. Ho già provato a utilizzare "nohup startCommand &" davanti a startCommand ma non ha funzionato come previsto.

Come una soluzione che ora utilizzare il seguente script bash per chiamare script di avvio dell'applicazione:

#!/bin/bash 

LOGFILE="/opt/scripts/bin/logs/SomeServerApplicationStart.log" 

nohup /opt/someDir/startSomeServerApplication.sh >${LOGFILE} 2>&1 & 

STARTUPOK=$(tail -1 ${LOGFILE} | grep "Server started in RUNNING mode" | wc -l) 
COUNTER=0 

while [ $STARTUPOK -ne 1 ] && [ $COUNTER -lt 100 ]; do 
    STARTUPOK=$(tail -1 logs/SomeServerApplicationStart.log | grep "Server started in RUNNING mode" | wc -l) 
    if ((STARTUPOK)); then 
     echo "STARTUP OK" 
     exit 0 
    fi 
    sleep 1 
    COUNTER=$(($COUNTER + 1)) 
done 

echo "STARTUP FAILED" 

Lo script bash è chiamata dal mio codice python. Questa soluzione funziona perfettamente, ma preferirei fare tutto in python ...

È un sottoprocesso.Popen nel modo sbagliato? Come potrei completare il mio compito solo in Python?

+1

probabilmente un doppio di http://stackoverflow.com/questions/1196074/starting- a-background-process-in-python – daTokenizer

+0

Basta usare 'communicate' quando è necessario .. non è necessario controllare i risultati? basta evitarlo ... – klashxx

+0

@klashxx: ho bisogno dei risultati. Questo è il mio problema ... (è necessario controllare l'output di "Server avviato in modalità RUNNING" ...) – Ronzo

risposta

1

Per prima cosa è facile non bloccare lo script Python nella comunicazione ... non chiamando la comunicazione! Basta leggere l'output o l'output dell'errore dal comando finché non trovi il messaggio corretto e dimentichi semplicemente il comando.

# to avoid waiting for an EOF on a pipe ... 
def getlines(fd): 
    line = bytearray() 
    c = None 
    while True: 
     c = fd.read(1) 
     if c is None: 
      return 
     line += c 
     if c == '\n': 
      yield str(line) 
      del line[:] 

p = subprocess.Popen(startCommand, shell=True, stdout=subprocess.PIPE, 
       stderr=subprocess.STDOUT) # send stderr to stdout, same as 2>&1 for bash 
for line in getlines(p.stdout): 
    if "Server started in RUNNING mode" in line: 
     print("STARTUP OK") 
     break 
else: # end of input without getting startup message 
    print("STARTUP FAILED") 
    p.poll() # get status from child to avoid a zombie 
    # other error processing 

Il problema con quanto sopra, è che il server è ancora un bambino un processo di Python e potrebbe ottenere segnali indesiderati quali SIGHUP. Se vuoi renderlo un demone, devi prima avviare un sottoprocesso che avvia il tuo server. In questo modo quando il primo figlio finirà, può essere aspettato dal chiamante e il server riceverà un PPID di 1 (adottato dal processo di init). È possibile utilizzare il modulo multiprocessing per facilitare quella parte

codice potrebbe essere come:

import multiprocessing 
import subprocess 

# to avoid waiting for an EOF on a pipe ... 
def getlines(fd): 
    line = bytearray() 
    c = None 
    while True: 
     c = fd.read(1) 
     if c is None: 
      return 
     line += c 
     if c == '\n': 
      yield str(line) 
      del line[:] 

def start_child(cmd): 
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
         shell=True) 
    for line in getlines(p.stdout): 
     print line 
     if "Server started in RUNNING mode" in line: 
      print "STARTUP OK" 
      break 
    else: 
     print "STARTUP FAILED" 

def main(): 
    # other stuff in program 
    p = multiprocessing.Process(target = start_child, args = (server_program,)) 
    p.start() 
    p.join() 
    print "DONE" 
    # other stuff in program 

# protect program startup for multiprocessing module 
if __name__ == '__main__': 
    main() 

ci si potrebbe chiedere che cosa è la necessità per il generatore getlines quando un oggetto del file è di per sé un iteratore che restituisce una riga alla tempo. Il problema è che chiama internamente read che legge fino a EOF quando il file non è connesso a un terminale. Come è ora collegato a un PIPE, non si otterrà nulla fino a quando il server non termina ... che è non quello che è previsto

+0

Perfetto! Grazie mille per la tua risposta. Ora so esattamente come farlo! – Ronzo

Problemi correlati