2014-05-23 2 views
3

У меня есть сценарий python с именем monitiq_install.py, который вызывает другие скрипты (или модули) с помощью модуля python subprocess. Однако, если пользователь отправляет прерывание клавиатуры (CTRL + C), он выходит, но с исключением. Я хочу, чтобы он вышел, но приятно.Обработка прерывания клавиатуры при использовании подпроцесса

Мой код:

import os 
import sys 
from os import listdir 
from os.path import isfile, join 
from subprocess import Popen, PIPE 
import json 

# Run a module and capture output and exit code 
def runModule(module): 
    try: 
     # Run Module 
     process = Popen(os.path.dirname(os.path.realpath(__file__)) + "/modules/" + module, shell=True, stdout=PIPE, bufsize=1) 
     for line in iter(process.stdout.readline, b''): 
      print line, 

     process.communicate() 
     exit_code = process.wait(); 

     return exit_code; 
    except KeyboardInterrupt: 
     print "Got keyboard interupt!"; 
     sys.exit(0); 

Ошибка я получаю ниже:

python monitiq_install.py -a 
Invalid module filename: create_db_user_v0_0_0.pyc 
Not Running Module: '3parssh_install' as it is already installed 
###################################### 
Running Module: 'create_db_user' Version: '0.0.3' 
Choose username for Monitiq DB User [MONITIQ] 
^CTraceback (most recent call last): 
    File "/opt/monitiq-universal/install/modules/create_db_user-v0_0_3.py", line 132, in <module> 
    inputVal = raw_input(""); 
Traceback (most recent call last): 
    File "monitiq_install.py", line 40, in <module> 
KeyboardInterrupt 
    module_install.runModules(); 
    File "/opt/monitiq-universal/install/module_install.py", line 86, in runModules 
    exit_code = runModule(module); 
    File "/opt/monitiq-universal/install/module_install.py", line 19, in runModule 
    for line in iter(process.stdout.readline, b''): 
KeyboardInterrupt 

Раствор или некоторые указатели будут полезны :)

--edit С попыткой уловить

Running Module: 'create_db_user' Version: '0.0.0' 
Choose username for Monitiq DB User [MONITIQ] 
^CGot keyboard interupt! 
Traceback (most recent call last): 
    File "monitiq_install.py", line 36, in <module> 
    module_install.runModules(); 
    File "/opt/monitiq-universal/install/module_install.py", line 90, in runModules 
    exit_code = runModule(module); 
    File "/opt/monitiq-universal/install/module_install.py", line 29, in runModule 
    sys.exit(0); 
NameError: global name 'sys' is not defined 
Traceback (most recent call last): 
    File "/opt/monitiq-universal/install/modules/create_db_user-v0_0_0.py", line 132, in <module> 
    inputVal = raw_input(""); 
KeyboardInterrupt 
+0

Есть ли у вас какие-либо трудности при исправлении: 'NameError: global name 'sys' не определен'? – jfs

ответ

2

Вы можете сделать это с помощью попробовать и, кроме как показано ниже:

import subprocess 
try: 
    proc = subprocess.Popen("dir /S", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
    while proc.poll() is None: 
     print proc.stdout.readline() 
except KeyboardInterrupt: 
    print "Got Keyboard interrupt" 

Вы могли бы избежать shell=True в вашем исполнении в качестве лучшей практики безопасности.

+0

Закрыть, но не сигары. См. Мое редактирование :) – woverton

+0

Я не думал о проблеме безопасности использования shell = True. Спасибо! – woverton

+0

похоже, что вы выполняете некоторый код внутри try, except и некоторый код снаружи. Можно ли показать код? – user3

3

Если вы нажмете Ctrl + C в терминале, тогда SIGINT отправляется всем процессам в группе процессов. См. child process receives parent's SIGINT.

Вот почему вы видите трассировку из дочернего процесса, несмотря на попытку/исключение KeyboardInterrupt в родительском.

Вы можете подавить выход stderr от дочернего процесса: stderr=DEVNULL. Или запустить его в новой группе процессов: start_new_session=True:

import sys 
from subprocess import call 

try: 
    call([sys.executable, 'child.py'], start_new_session=True) 
except KeyboardInterrupt: 
    print('Ctrl C') 
else: 
    print('no exception') 

Если удалить start_new_session=True в приведенном выше примере, то KeyboardInterrupt может быть поднято в ребенке тоже, и вы могли бы получить отслеживающие.

Если subprocess.DEVNULL не доступен; вы можете использовать DEVNULL = open(os.devnull, 'r+b', 0). Если параметр start_new_session недоступен; вы можете использовать preexec_fn=os.setsid на POSIX.

Смежные вопросы