पायथन में SIGINT को कैसे कैप्चर करूं?


534

मैं एक अजगर स्क्रिप्ट पर काम कर रहा हूं जो कई प्रक्रियाओं और डेटाबेस कनेक्शनों को शुरू करती है। हर अब और फिर मैं स्क्रिप्ट को Ctrl+ Cसिग्नल के साथ मारना चाहता हूं , और मैं कुछ सफाई करना चाहता हूं।

पर्ल में मैं यह करूँगा:

$SIG{'INT'} = 'exit_gracefully';

sub exit_gracefully {
    print "Caught ^C \n";
    exit (0);
}

मैं पायथन में इस का एनालॉग कैसे करूं?

जवाबों:


786

अपने हैंडलर को signal.signalइस तरह पंजीकृत करें :

#!/usr/bin/env python
import signal
import sys

def signal_handler(sig, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()

यहां से कोड अनुकूलित किया गया

पर अधिक प्रलेखन यहाँsignal पाया जा सकता है ।  


13
क्या आप मुझे बता सकते हैं कि KeyboardInterrupt अपवाद के बजाय इसका उपयोग क्यों करें? उपयोग करने के लिए अधिक सहज नहीं है?
14

35
Noio: 2 कारण। सबसे पहले, SIGINT को आपकी प्रक्रिया में किसी भी तरीके से भेजा जा सकता है (जैसे, 'मार -s INT <pid>'); मुझे यकीन नहीं है कि अगर KeyboardInterruptException को SIGINT हैंडलर के रूप में लागू किया गया है या यदि यह वास्तव में केवल Ctrl + C दबाता है, लेकिन किसी भी तरह, सिग्नल हैंडलर का उपयोग करने से आपका इरादा स्पष्ट हो जाता है (कम से कम, यदि आपका इरादा ओपी के समान है)। अधिक महत्वपूर्ण बात यह है कि, एक संकेत के साथ, आपको उन्हें काम करने के लिए हर चीज के आसपास ट्राइ-कैच को लपेटना नहीं पड़ता है, जो आपके एप्लिकेशन की संरचना के आधार पर एक कंपोजिबिलिटी और सामान्य सॉफ़्टवेयर इंजीनियरिंग जीत के कम या ज्यादा हो सकते हैं।
मैट जे

35
उदाहरण के लिए आप अपवाद को पकड़ने के बजाय सिग्नल को क्यों फंसाना चाहते हैं। मान लें कि आप अपना प्रोग्राम चलाते हैं और आउटपुट को लॉग फ़ाइल में रीडायरेक्ट करते हैं ./program.py > output.log। जब आप Ctrl-C दबाते हैं, तो आप चाहते हैं कि आपका प्रोग्राम यह लॉग इन करके सभी डेटा फ़ाइलों को फ्लश और चिन्हित करने के लिए साफ-सुथरे तरीके से बाहर निकल जाए, यह पुष्टि करने के लिए कि उन्हें एक अच्छे राज्य में छोड़ दिया गया है। लेकिन Ctrl-C एक पाइपलाइन में सभी प्रक्रियाओं के लिए SIGINT भेजता है, इसलिए शेल अंतिम प्रोग्राम को प्रिंट करने से पहले program.py खत्म होने से पहले STDOUT (अब "output.log") को बंद कर सकता है। अजगर शिकायत करेगा, "फ़ाइल ऑब्जेक्ट डिस्ट्रक्टर में करीब विफल: sys.excepthook में त्रुटि:"।
नूह स्प्रियर

24
ध्यान दें कि Windows पर संकेत। Pause () अनुपलब्ध है। docs.python.org/dev/library/signal.html
May

10
-1. सिग्नल का उपयोग करने के लिए इकसिंगों (), से पता चलता है कि मुझे कुछ वास्तविक काम करने के बजाय ऐसी अवरुद्ध कॉल पर इंतजार करना होगा। ;)
निक टी

177

आप इसे किसी अन्य की तरह एक अपवाद (KeyboardInterrupt) मान सकते हैं। एक नई फ़ाइल बनाएं और इसे अपने शेल से चलाएं, जो कि मेरा मतलब है यह देखने के लिए निम्नलिखित सामग्री के साथ है:

import time, sys

x = 1
while True:
    try:
        print x
        time.sleep(.3)
        x += 1
    except KeyboardInterrupt:
        print "Bye"
        sys.exit()

22
इस समाधान का उपयोग करते समय ध्यान दें। आप KeyboardInterrupt कैच ब्लॉक से पहले भी इस कोड का उपयोग करना चाहिए: signal.signal(signal.SIGINT, signal.default_int_handler), या आप विफल क्योंकि KeyboardInterrupt हर स्थिति है जिसमें यह सक्रिय किया जाना चाहिए में आग नहीं करता जा रहे हैं,! विवरण यहाँ हैं
वेल्डा

67

और एक संदर्भ प्रबंधक के रूप में:

import signal

class GracefulInterruptHandler(object):

    def __init__(self, sig=signal.SIGINT):
        self.sig = sig

    def __enter__(self):

        self.interrupted = False
        self.released = False

        self.original_handler = signal.getsignal(self.sig)

        def handler(signum, frame):
            self.release()
            self.interrupted = True

        signal.signal(self.sig, handler)

        return self

    def __exit__(self, type, value, tb):
        self.release()

    def release(self):

        if self.released:
            return False

        signal.signal(self.sig, self.original_handler)

        self.released = True

        return True

काम में लाना:

with GracefulInterruptHandler() as h:
    for i in xrange(1000):
        print "..."
        time.sleep(1)
        if h.interrupted:
            print "interrupted!"
            time.sleep(2)
            break

नेस्टेड हैंडलर:

with GracefulInterruptHandler() as h1:
    while True:
        print "(1)..."
        time.sleep(1)
        with GracefulInterruptHandler() as h2:
            while True:
                print "\t(2)..."
                time.sleep(1)
                if h2.interrupted:
                    print "\t(2) interrupted!"
                    time.sleep(2)
                    break
        if h1.interrupted:
            print "(1) interrupted!"
            time.sleep(2)
            break

यहां से: https://gist.github.com/2907502


StopIterationजब एक ctrl-C दबाया जाता है, तो यह अंतरतम लूप को तोड़ने के लिए भी फेंक सकता है, है ना?
थियो बेलैयर

@ Theoelaire सिर्फ स्टॉपइंटरनेशन को फेंकने के बजाय, मैं एक जनरेटर बनाऊंगा जो एक पैरामीटर के रूप में एक पुनरावृत्ति को स्वीकार करता है और सिग्नल हैंडलर को पंजीकृत / रिलीज करता है।
उदी १

28

आप संभाल कर सकते हैं CTRL+ Cपकड़ने से KeyboardInterruptअपवाद। आप अपवाद हैंडलर में कोई भी सफाई कोड लागू कर सकते हैं।



18

फिर भी एक और स्निपेट

mainमुख्य कार्य के exit_gracefullyरूप में और CTRL+ cहैंडलर के रूप में संदर्भित

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pass
    finally:
        exit_gracefully()

4
आपको केवल उस सामान को छोड़कर उपयोग करना चाहिए जो होने वाला नहीं है। इस मामले में KeyboardInterrupt हो जाता है। इसलिए यह एक अच्छा निर्माण नहीं है।
ट्रिस्टन

15
@TristanT किसी अन्य भाषा में हाँ, लेकिन पायथन अपवादों में केवल उन चीजों के लिए नहीं हैं जो होने वाली नहीं हैं। प्रवाह नियंत्रण (जहां उपयुक्त हो) के अपवादों का उपयोग करना वास्तव में पायथन में अच्छी शैली माना जाता है।
इयान गोल्डी ने

8

मैंने कई संकेतों (कुछ भी नहीं फैंसी) का समर्थन करने के लिए @udi से कोड अनुकूलित किया:

class GracefulInterruptHandler(object):
    def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
        self.signals = signals
        self.original_handlers = {}

    def __enter__(self):
        self.interrupted = False
        self.released = False

        for sig in self.signals:
            self.original_handlers[sig] = signal.getsignal(sig)
            signal.signal(sig, self.handler)

        return self

    def handler(self, signum, frame):
        self.release()
        self.interrupted = True

    def __exit__(self, type, value, tb):
        self.release()

    def release(self):
        if self.released:
            return False

        for sig in self.signals:
            signal.signal(sig, self.original_handlers[sig])

        self.released = True
        return True

इस कोड को कॉल बाधा कुंजीपटल (समर्थन SIGINT) और SIGTERM( kill <process>)


5

के विपरीत मैट जम्मू उसके जवाब है, मैं एक साधारण वस्तु का उपयोग करें। इससे मुझे इस हैंडलर को उन सभी धागों को पार्स करने का अधिकार मिल गया है, जिन्हें सिक्योररी को रोकने की आवश्यकता है।

class SIGINT_handler():
    def __init__(self):
        self.SIGINT = False

    def signal_handler(self, signal, frame):
        print('You pressed Ctrl+C!')
        self.SIGINT = True


handler = SIGINT_handler()
signal.signal(signal.SIGINT, handler.signal_handler)

अन्यत्र

while True:
    # task
    if handler.SIGINT:
        break

आपको एक घटना का उपयोग करना चाहिए या time.sleep()एक चर पर एक व्यस्त लूप करने के बजाय।
ओलिवियरएम

@OlivierM यह वास्तव में विशिष्ट अनुप्रयोग है और निश्चित रूप से इस उदाहरण का बिंदु नहीं है। उदाहरण के लिए, कॉल या प्रतीक्षा कार्यों को अवरुद्ध करना सीपीयू को व्यस्त नहीं रखेगा। इसके अलावा, यह सिर्फ एक उदाहरण है कि चीजें कैसे की जा सकती हैं। KeyboardInterrupts अक्सर पर्याप्त अन्य उत्तर में उल्लेख किया है की तरह कर रहे हैं।
थॉमस देववोग्ट

4

आप अजगर में सिग्नल हैंडलर स्थापित करने के लिए पायथन के अंतर्निहित सिग्नल मॉड्यूल में कार्यों का उपयोग कर सकते हैं । विशेष signal.signal(signalnum, handler)रूप से handlerफ़ंक्शन को सिग्नल के लिए फ़ंक्शन को पंजीकृत करने के लिए उपयोग किया जाता है signalnum


3

मौजूदा जवाब के लिए धन्यवाद, लेकिन जोड़ा signal.getsignal()

import signal

# store default handler of signal.SIGINT
default_handler = signal.getsignal(signal.SIGINT)
catch_count = 0

def handler(signum, frame):
    global default_handler, catch_count
    catch_count += 1
    print ('wait:', catch_count)
    if catch_count > 3:
        # recover handler for signal.SIGINT
        signal.signal(signal.SIGINT, default_handler)
        print('expecting KeyboardInterrupt')

signal.signal(signal.SIGINT, handler)
print('Press Ctrl+c here')

while True:
    pass

3

यदि आप यह सुनिश्चित करना चाहते हैं कि आपकी क्लीनअप प्रक्रिया समाप्त हो जाए तो मैं SIG_IGN का उपयोग करके मैट जे के उत्तर को जोड़ दूंगा ताकि आगे की अनदेखी की जाए जो आपके क्लीनअप को बाधित होने से रोके।SIGINT

import signal
import sys

def signal_handler(signum, frame):
    signal.signal(signum, signal.SIG_IGN) # ignore additional signals
    cleanup() # give your process a chance to clean up
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler) # register the signal with the signal handler first
do_stuff()

0

निजी तौर पर, मैं KeyboardInterrupt को छोड़कर / कोशिश का उपयोग नहीं कर सका क्योंकि मैं मानक सॉकेट (IPC) मोड का उपयोग कर रहा था जो अवरुद्ध है। इसलिए SIGINT को cueued किया गया था, लेकिन सॉकेट पर डेटा प्राप्त करने के बाद ही आया था।

सिग्नल हैंडलर सेट करना एक समान व्यवहार करता है।

दूसरी ओर, यह केवल एक वास्तविक टर्मिनल के लिए काम करता है। अन्य आरंभिक वातावरण संकेत को Ctrl+ Cया पूर्व-संभाल को स्वीकार नहीं कर सकते हैं ।

इसके अलावा, पायथन में "अपवाद" और "बेसएक्स अपवाद" हैं, जो इस अर्थ में भिन्न हैं कि दुभाषिया को खुद से बाहर निकलने की आवश्यकता होती है, इसलिए कुछ अपवादों की तुलना में दूसरों की तुलना में अधिक प्राथमिकता होती है (अपवाद बेसएक्ससेप्शन से लिया गया है)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.