পাইথনে আমি কীভাবে সাইন্ট ক্যাপচার করব?


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
আপনি কি আমাকে বলতে পারবেন কেন এটি একটি কী-বোর্ড আন্তঃবিধ ব্যতিক্রমের স্থলে ব্যবহার করবেন? এটি ব্যবহার করার মতো আরও স্বজ্ঞাত নয় কি?
noio

35
নওও: 2 টি কারণ। প্রথমত, সাইন ইন আপনার প্রক্রিয়ায় যেকোন উপায়ে প্রেরণ করা যেতে পারে (যেমন, 'কিল-গুলি INT <পিড>'); আমি নিশ্চিত নই যে কী-বোর্ড আন্তঃপরীক্ষাটি একটি সাইন্ট হ্যান্ডলার হিসাবে প্রয়োগ করা হয়েছে বা যদি এটি সত্যিই কেবল সিটিআরএল + সি টিপে তবে এটি যে কোনও উপায়ে সিগন্যাল হ্যান্ডলারটি ব্যবহার করে আপনার উদ্দেশ্যকে সুস্পষ্ট করে তোলে (কমপক্ষে, যদি আপনার উদ্দেশ্যটি ওপি'র মতো হয়)। আরও গুরুত্বপূর্ণ, যদিও আপনাকে একটি সিগন্যালের সাহায্যে এগুলিকে কাজ করতে সমস্ত কিছুর আশপাশে ট্র্যাচ-ক্যাচগুলি আবৃত করতে হবে না, যা আপনার অ্যাপ্লিকেশনটির কাঠামোর উপর নির্ভর করে কমপক্ষে সামঞ্জস্যযোগ্যতা এবং সাধারণ সফ্টওয়্যার ইঞ্জিনিয়ারিং জয়ের বেশি বা কম হতে পারে।
ম্যাট জে

35
আপনি কেন ব্যতিক্রম ধরার পরিবর্তে সিগন্যালটি ফাঁদে ফেলতে চান তার উদাহরণ। বলুন আপনি নিজের প্রোগ্রামটি চালাচ্ছেন এবং আউটপুটটিকে একটি লগ ফাইলে পুনর্নির্দেশ করুন ./program.py > output.log,। আপনি যখন Ctrl-C টিপেন তখন আপনি চান যে আপনার প্রোগ্রামটি সুপরিচিতভাবে প্রস্থান করতে পারে যাতে লগ হয় যে সমস্ত ডেটা ফাইল ফ্লো করে ফেলেছে এবং পরিষ্কার হিসাবে চিহ্নিত করা হয়েছে যাতে সেগুলি একটি ভাল ভাল অবস্থায় রেখে গেছে confirm তবে সিটিআরএল-সি একটি পাইপলাইনে সমস্ত প্রক্রিয়াতে সিগিন্ট প্রেরণ করে, সুতরাং প্রোগ্রাম.পি চূড়ান্ত লগ মুদ্রণ শেষ করার আগে শেলটি STDOUT (এখন "আউটপুট.লগ") বন্ধ করে দিতে পারে। পাইথন অভিযোগ করবে, "ফাইল অবজেক্ট ডেস্ট্রাক্টরে ক্লোজ ব্যর্থ: সিএস.এক্সপসথੁੱਕতে ত্রুটি:"।
নোহ স্পুরিয়ার

24
মনে রাখবেন যে সিগন্যাল.পজ () উইন্ডোজটিতে অনুপলব্ধ। docs.python.org/dev/library/signal.html

10
সিগন্যাল.পেজ () ব্যবহারের জন্য -1 ইউনিকর্নস, পরামর্শ দেয় যে আমাকে কিছু বাস্তব কাজ করার পরিবর্তে এই জাতীয় ব্লকিং কলটিতে অপেক্ষা করতে হবে। ;)
নিক টি

177

আপনি এটিকে অন্য যে কোনও ব্যতিক্রমের (কী-বোর্ড ইন্টারটার্ট) মতো আচরণ করতে পারেন। আমার অর্থটি বোঝার জন্য একটি নতুন ফাইল তৈরি করুন এবং আপনার শেল থেকে নিম্নলিখিত সামগ্রীগুলি সহ এটি চালান:

import time, sys

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

22
এই সমাধানটি ব্যবহার করার সময় মনোযোগ দিন। কীবোর্ড আন্তঃবিড়িত ক্যাচ ব্লক করার আগে আপনারও এই কোডটি ব্যবহার করা উচিত: signal.signal(signal.SIGINT, signal.default_int_handler)বা আপনি ব্যর্থ হতে চলেছেন, কারণ কীবোর্ড ইন্টারটার্ট প্রতিটি পরিস্থিতিতেই গুলি চালায় না! বিশদ এখানে
ভেলদা

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যখন কোনও সিটিআরএল-সি টিপছে, তখন ভিতরের লুপটি ভেঙে ফেলতে পারে ?
থিও বেলারে

@ থিওব্লেয়ার কেবল স্টপইটারেশন নিক্ষেপ করার পরিবর্তে আমি এমন একটি জেনারেটর তৈরি করব যা পুনরাবৃত্তিকে প্যারামিটার হিসাবে গ্রহণ করে এবং সিগন্যাল হ্যান্ডলারের নিবন্ধ / নিবন্ধ প্রকাশ করে।
উদি

28

ব্যতিক্রমটি ধরে আপনি CTRL+ পরিচালনা করতে পারবেন । আপনি ব্যতিক্রম হ্যান্ডলারটিতে কোনও ক্লিন-আপ কোড প্রয়োগ করতে পারেন।CKeyboardInterrupt



18

তবুও অন্য স্নিপেট

mainমূল ফাংশন এবং + হ্যান্ডলার exit_gracefullyহিসাবে উল্লেখ করা হয়CTRLc

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

4
আপনার কেবল এমন স্টাফ ব্যতীত ব্যবহার করা উচিত যা ঘটেছিল বলে মনে হয় না। এক্ষেত্রে কী-বোর্ড ইন্টারটারপেট হওয়ার কথা রয়েছে। সুতরাং এটি একটি ভাল নির্মাণ নয়।
ত্রিস্তান

15
@ ত্রিস্তানটি অন্য যে কোনও ভাষায় হ্যাঁ, তবে পাইথনের ব্যতিক্রমগুলি কেবল এমন জিনিসগুলির জন্য নয় যা ঘটেছিল বলে মনে করা হয় না। প্রবাহ নিয়ন্ত্রণ (যেখানে উপযুক্ত) সেখানে ব্যতিক্রম ব্যবহার করার জন্য পাইথনে এটি আসলে ভাল স্টাইল হিসাবে বিবেচিত হয়।
ইয়ান গোল্ডবি

8

আমি একাধিক সংকেত সমর্থন করার জন্য @ অডি থেকে কোডটি রূপান্তর করেছি (অভিনব কিছুই নয়):

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()পরিবর্তে একটি ভেরিয়েবলের উপর ব্যস্ত লুপটি করা উচিত।
অলিভিএমএম

@ অলিভিএম এটি সত্যিই অ্যাপ্লিকেশন নির্দিষ্ট এবং অবশ্যই এই উদাহরণটির মূল বিষয় নয়। উদাহরণস্বরূপ, কলগুলি ব্লক করা বা ফাংশনগুলির জন্য অপেক্ষা করা সিপিইউকে ব্যস্ত রাখবে না। তদুপরি, এটি কীভাবে জিনিসগুলি করা যায় তার একটি উদাহরণ। অন্যান্য জবাব হিসাবে উল্লিখিত কীবোর্ডআইন্ট্রাপগুলি প্রায়শই যথেষ্ট।
থমাস ডিভোগডিট

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

ব্যক্তিগতভাবে, আমি কীবোর্ড আন্তঃবিধ ব্যতীত চেষ্টা করে / ব্যবহার করতে পারিনি কারণ আমি স্ট্যান্ডার্ড সকেট (আইপিসি) মোড ব্যবহার করছিলাম যা ব্লক করছে। সুতরাং সিগিন্টটি সন্ধান করা হয়েছিল, তবে কেবল সকেটে ডেটা পাওয়ার পরে এসেছিল।

একটি সিগন্যাল হ্যান্ডলার সেট করা একই আচরণ করে।

অন্যদিকে, এটি কেবল আসল টার্মিনালের জন্য কাজ করে। আরম্ভ করা অন্যান্য পরিবেশগুলি হয়ত Ctrl+ গ্রহণ করবে না Cবা সিগন্যালটিকে প্রাক-হ্যান্ডেল করবে।

এছাড়াও পাইথনে "ব্যতিক্রম" এবং "বেসএক্সেপশনস" রয়েছে, যে অর্থটি ব্যাখ্যা করে যে দোভাষীকে পরিষ্কারভাবে নিজেই বেরিয়ে আসা দরকার, তাই কিছু ব্যতিক্রমের তুলনায় অন্যদের তুলনায় উচ্চতর অগ্রাধিকার থাকে (ব্যতিক্রম বেসেক্সেক্সেপশন থেকে নেওয়া)

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.