2015-11-19 56 views
5

Obecnie piszę mój pierwszy program Pythona (w Pythonie 2.6.6). Program ułatwia uruchamianie i zatrzymywanie różnych aplikacji uruchomionych na serwerze, udostępniając użytkownikowi typowe polecenia (takie jak uruchamianie i zatrzymywanie usług systemowych na serwerze Linux).Jak mogę uruchomić proces i umieścić go w tle w pythonie?

Zaczynam skrypty startowe Applications' przez

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

Problem polega na tym, że skrypt startowy z jednej aplikacji pozostaje na pierwszym planie, a więc p.communicate() czeka na zawsze. Próbowałem już użyć "nohup startCommand &" przed komendą startCommand, ale to nie działało zgodnie z oczekiwaniami.

Jako obejście Teraz używam następujący skrypt bash aby zadzwonić skrypt uruchomić aplikację za:

#!/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" 

Skrypt bash jest wywoływana z mojego kodu Pythona. To obejście działa idealnie, ale wolałbym zrobić wszystko w pythonie ...

Czy podproces.Popen w niewłaściwy sposób? W jaki sposób mogę dołączyć do zadania tylko w Pythonie?

+1

prawdopodobnie dupkiem http://stackoverflow.com/questions/1196074/starting- a-background-process-in-python – daTokenizer

+0

Po prostu użyj komendy 'communic', kiedy jest to potrzebne ... nie musisz sprawdzać wyników? po prostu tego unikaj ... – klashxx

+0

@klashxx: Potrzebuję wyników. To jest mój problem ... (trzeba sprawdzić wyjście dla "Serwer uruchomiony w trybie RUNNING" ...) – Ronzo

Odpowiedz

1

Po pierwsze nie jest łatwo nie blokować skryptu Python w komunikacji ... przez nie wywoływanie komunikacji! Po prostu odczytaj dane wyjściowe lub wyjście błędów z polecenia, aż znajdziesz właściwy komunikat i po prostu zapomnij o poleceniu.

# 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 

Problem z powyższego jest to, że serwer jest nadal dziecku procesu Python i może dostać niepożądane sygnały, takie jak SIGHUP. Jeśli chcesz, aby był demonem, musisz najpierw uruchomić podproces, który następnie uruchom serwer. W ten sposób, gdy pierwsze dziecko się zakończy, może czekać na niego dzwoniący, a serwer otrzyma identyfikator PPID 1 (przyjęty przez proces init). Można użyć modułu Multiprocessing złagodzić że part

kod może być jak:

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

Można by się zastanawiać, co jest potrzeba generatora getlines gdy obiekt pliku jest również iterator, które zwraca jeden wiersz w czas. Problem polega na tym, że wewnętrznie wywołuje on read, który czyta do EOF, gdy plik nie jest podłączony do terminala. Ponieważ jest on teraz połączony z PIPE, nic nie dostaniesz, dopóki serwer się nie skończy ... co jest nie, co jest oczekiwane,

+0

Idealny! Bardzo dziękuję za odpowiedź. Teraz wiem dokładnie, jak to zrobić! – Ronzo