जब मैं इसे Ctrl + C करता हूं तो मैं शेल स्क्रिप्ट में पृष्ठभूमि प्रक्रियाओं को कैसे मार सकता हूं और इंतजार कर सकता हूं?


24

मैं एक शेल स्क्रिप्ट स्थापित करने की कोशिश कर रहा हूं ताकि यह पृष्ठभूमि प्रक्रियाओं को चलाए, और जब मैं Ctrlcशेल स्क्रिप्ट करता हूं , तो यह बच्चों को मारता है, फिर बाहर निकलता है।

सबसे अच्छा है कि मैं इसके साथ आने में कामयाब रहा हूं। ऐसा प्रतीत होता है कि kill 0 -INTप्रतीक्षा पूरी होने से पहले ही स्क्रिप्ट को मार देता है, इसलिए शेल स्क्रिप्ट पूरी होने से पहले ही मर जाती है।

इस बारे में कोई विचार कि मैं इस शेल लिपि को बच्चों को भेजने के बाद मरने की प्रतीक्षा कैसे कर सकता हूं INT?

#!/bin/bash
trap 'killall' INT

killall() {
    echo "**** Shutting down... ****"
    kill 0 -INT
    wait # Why doesn't this wait??
    echo DONE
}

process1 &
process2 &
process3 &

cat # wait forever


मैंने "शेल इंटरसेप्ट किल स्विच" खोजकर यह प्रश्न पाया, ट्रैप कमांड को साझा करने के लिए धन्यवाद, ऐप को Ctrl + C के साथ बाहर निकलने के बाद एक कमांड निष्पादित करने के लिए बहुत उपयोगी है जब ऐप करीब GUI बटन के माध्यम से सही तरीके से बाहर नहीं निकलता है।
बपतिस्मा

जवाबों:


22

आपकी killआज्ञा पीछे की ओर है।

कई यूनिक्स आज्ञाओं की तरह, एक विकल्प जो माइनस से शुरू होता है, अन्य तर्कों से पहले पहले आना चाहिए।

अगर आप लिखेंगे

kill -INT 0

यह -INTएक विकल्प के रूप में देखता है , और वर्तमान प्रक्रिया समूह में सभी प्रक्रियाओं के SIGINTलिए भेजता है 0( 0एक विशेष संख्या का अर्थ है)।

लेकिन अगर आप लिखेंगे

kill 0 -INT

यह देखता है 0, निर्णय लेता है कि कोई और विकल्प नहीं है, इसलिए SIGTERMडिफ़ॉल्ट रूप से उपयोग करता है। और वर्तमान प्रक्रिया समूह को भेजता है , जैसा कि आपने किया था

kill -TERM 0 -INT    

(यह भी भेजने की कोशिश करेगा SIGTERMकरने के लिए -INTहै, जो एक सिंटैक्स त्रुटि का कारण होता है, लेकिन यह भेजता है SIGTERMके लिए 0पहले, और हो जाता है कभी नहीं है कि अब तक।)

तो आपका मुख्य स्क्रिप्ट एक हो रही है SIGTERMइससे पहले कि इसे चलाने के लिए हो जाता है waitऔर echo DONE

जोड़ना

trap 'echo got SIGTERM' TERM

शीर्ष पर, बस के बाद

trap 'killall' INT

और इसे साबित करने के लिए इसे फिर से चलाएं।

जैसा कि स्टीफन चेज़ेलस बताते हैं, आपके पृष्ठभूमि वाले बच्चे ( process1आदि) SIGINTडिफ़ॉल्ट रूप से अनदेखा कर देंगे ।

किसी भी मामले में, मुझे लगता है कि भेजने से SIGTERMअधिक समझ में आएगा।

अंत में, मुझे यकीन नहीं है कि kill -process groupपहले बच्चों को जाने की गारंटी है या नहीं । बंद करते समय संकेतों को अनदेखा करना एक अच्छा विचार हो सकता है।

तो यह प्रयास करें:

#!/bin/bash
trap 'killall' INT

killall() {
    trap '' INT TERM     # ignore INT and TERM while shutting down
    echo "**** Shutting down... ****"     # added double quotes
    kill -TERM 0         # fixed order, send TERM not INT
    wait
    echo DONE
}

./process1 &
./process2 &
./process3 &

cat # wait forever

धन्यवाद, यह काम करता है! प्रतीत होता है कि समस्या है।
फिसला

9

दुर्भाग्य से, पृष्ठभूमि में शुरू किए गए कमांड SIGINT को अनदेखा करने के लिए शेल द्वारा सेट किए गए हैं, और इससे भी बदतर, वे इसके साथ अन-अनदेखा नहीं कर सकते trap। अन्यथा, आपको बस इतना करना है

(trap - INT; exec process1) &
(trap - INT; exec process2) &
trap '' INT
wait

क्योंकि जब आप Ctrl-C दबाते हैं तो process1 और process2 को SIGINT मिलेगा क्योंकि वे उसी प्रक्रिया समूह का हिस्सा हैं जो टर्मिनल का अग्रभूमि प्रक्रिया समूह है।

उपरोक्त कोड pdksh और zsh के साथ काम करेगा जो उस संबंध में POSIX अनुरूप नहीं हैं।

अन्य गोले के साथ, आपको SIGINT के लिए डिफ़ॉल्ट हैंडलर को पुनर्स्थापित करने के लिए कुछ और का उपयोग करना होगा:

perl -e '$SIG{INT}=DEFAULT; exec "process1"' &

या SIGTERM जैसे एक अलग सिग्नल का उपयोग करें।


2

उन लोगों के लिए जो सिर्फ एक प्रक्रिया को मारना चाहते हैं और इसके मरने की प्रतीक्षा करते हैं, लेकिन अनिश्चित काल के लिए नहीं :

यह प्रति सिग्नल प्रकार की अधिकतम 60 सेकंड प्रतीक्षा करता है।

चेतावनी: यह जवाब किसी भी तरह से एक सिग्नल को फंसाने और इसे भेजने से संबंधित नहीं है।

# close_app_sub GREP_STATEMENT SIGNAL DURATION_SEC
# GREP_STATEMENT must not match itself!
close_app_sub() {
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ ! -z "$APP_PID" ]; then
        echo "App is open. Trying to close app (SIGNAL $2). Max $3sec."
        kill $2 "$APP_PID"
        WAIT_LOOP=0
        while ps -p "$APP_PID" > /dev/null 2>&1; do
            sleep 1
            WAIT_LOOP=$((WAIT_LOOP+1))
            if [ "$WAIT_LOOP" = "$3" ]; then
                break
            fi
        done
    fi
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ -z "$APP_PID" ]; then return 0; else return "$APP_PID"; fi
}

close_app() {
    close_app_sub "$1" "-HUP" "60"
    close_app_sub "$1" "-TERM" "60"
    close_app_sub "$1" "-SIGINT" "60"
    close_app_sub "$1" "-KILL" "60"
    return $?
}

close_app "[f]irefox"

यह नाम या तर्कों से मारने के लिए ऐप का चयन करता है। मिलान नाम से बचने के लिए ऐप नाम के पहले अक्षर के लिए कोष्ठक रखें।

कुछ परिवर्तनों के साथ, आप कथन के pidof process_nameबजाय सीधे PID या सरल का उपयोग कर सकते हैं ps

कोड विवरण: अंतिम grep ट्रेलिंग रिक्त स्थान के बिना PID प्राप्त करना है।


1

यदि आप जो चाहते हैं वह कुछ पृष्ठभूमि प्रक्रियाओं को प्रबंधित करने के लिए है, तो बैश नौकरी नियंत्रण सुविधाओं का उपयोग क्यों नहीं करना है?

$ gedit &
[1] 2581
$ emacs &
[2] 2594
$ jobs
[1]-  Running                 gedit &
[2]+  Running                 emacs &
$ jobs -p
2581
2594
$ kill -2 `jobs -p`
$ jobs
[1]-  Interrupt               gedit
[2]+  Done                    emacs

0

इसलिए मैंने इसके साथ-साथ चारों ओर टिंकर किया। बैश में, आप कार्यों को परिभाषित कर सकते हैं और उन लोगों के साथ फैंसी चीजें कर सकते हैं। मेरे सहकर्मी और मैं उपयोग करते हैं terminator, इसलिए बैच निष्पादन के लिए हम आम तौर पर टर्मिनेटर विंडो का एक पूरा गुच्छा स्पॉन करते हैं यदि हम आउटपुट देखना चाहते हैं ( tmuxटैब से कम सुरुचिपूर्ण , लेकिन आप एक अधिक जीयूआई-जैसे इंटरफ़ेस हा का उपयोग कर सकते हैं)।

यहाँ सेटअप है कि मैं के साथ आया था, साथ ही सामान का एक उदाहरण आप चला सकते हैं:

#!/bin/bash
custom()
{
    terun 'something cool' 'yes'
    run 'xclock'
    terun 'echoes' 'echo Hello; echo Goodbye; read'
    func()
    {
        local i=0
        while :
        do
            echo "Iter $i"
            let i+=1
            sleep 0.25
        done
    }
    export -f func
    terun 'custom func' 'func'
}

# [ Setup ]
run()
{
    "$@" &
    # Give process some time to start up so windows are sequential
    sleep 0.05
}
terun()
{
    run terminator -T "$1" -e "$2"
}
finish()
{
    procs="$(jobs -p)"
    echo "Kill: $procs"
    # Ignore process that are already dead
    kill $procs 2> /dev/null
}
trap 'finish' 2

custom

echo 'Press <Ctrl+C> to kill...'
wait

इसे चला रहे हैं

$ ./program_spawner.sh 
Press <Ctrl+C> to kill...
^CKill:  9835
9840
9844
9850
$ 

@Maxim को संपादित करें , बस अपने सुझाव को देखा, जो इसे एक टन सरल बनाता है! धन्यवाद!

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