जब मेरी शेल स्क्रिप्ट बाहर निकलती है तो मैं पृष्ठभूमि प्रक्रियाओं / नौकरियों को कैसे मार सकता हूं?


193

मैं अपने शीर्ष स्तर की स्क्रिप्ट से बाहर निकलने पर गंदगी को साफ करने का रास्ता तलाश रहा हूं।

विशेष रूप से अगर मैं उपयोग करना set -eचाहता हूं, तो मैं चाहता हूं कि जब स्क्रिप्ट बाहर निकल जाएगी तो पृष्ठभूमि की प्रक्रिया मर जाएगी।

जवाबों:


186

कुछ गंदगी को साफ करने के लिए trapइस्तेमाल किया जा सकता है। यह एक विशिष्ट सिग्नल आने पर निष्पादित सामान की सूची प्रदान कर सकता है:

trap "echo hello" SIGINT

लेकिन यह भी इस्तेमाल किया जा सकता है कि कुछ बाहर निकलता है तो शेल बाहर निकलता है:

trap "killall background" EXIT

यह एक बिलिन है, इसलिए help trapआपको जानकारी देगा (काम के साथ काम करता है)। यदि आप केवल बैकग्राउंड जॉब्स को मारना चाहते हैं, तो आप कर सकते हैं

trap 'kill $(jobs -p)' EXIT

सिंगल का उपयोग 'करने के लिए देखें, शेल को $()तुरंत प्रतिस्थापित करने से रोकने के लिए ।


फिर आप बच्चे को कैसे मारते हैं ? (या मैं कुछ स्पष्ट याद कर रहा हूँ)
elmarco


4
kill $(jobs -p)डैश में काम नहीं करता है, क्योंकि यह एक सबशेल में कमांड प्रतिस्थापन को निष्पादित करता है (मैन डैश में कमांड
सब्स्टीट्यूशन देखें

8
है killall backgroundएक प्लेसहोल्डर होना चाहिए? backgroundमैन पेज में नहीं है ...
इवान बेने

170

यह मेरे लिए काम करता है (टिप्पणीकारों के लिए धन्यवाद में सुधार):

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
  • kill -- -$$पूरी प्रक्रिया समूह को एक SIGTERM भेजता है , इस प्रकार यह भी वंशज को मारता है।

  • निर्दिष्ट संकेत EXITका उपयोग करते समय उपयोगी है set -e(अधिक विवरण यहां )।


1
पूरी तरह से अच्छी तरह से काम करना चाहिए, लेकिन बच्चे प्रक्रिया समूह को बदल सकते हैं। दूसरी ओर इसे नौकरी पर नियंत्रण की आवश्यकता नहीं है, और अन्य समाधानों द्वारा कुछ पोते की प्रक्रियाओं को याद किया जा सकता है।
माइकलजेट

5
ध्यान दें, "किल 0" एक पैरेंट बैश स्क्रिप्ट को भी मार देगा। आप वर्तमान स्क्रिप्ट के केवल बच्चों को मारने के लिए "किल - - $ BASHPID" का उपयोग करना चाह सकते हैं। यदि आपके पास अपने bash संस्करण में $ BASHPID नहीं है, तो आप BASHPID = $ (sh -c 'echo $ PPID') निर्यात कर सकते हैं
ACIClic

2
अच्छे और स्पष्ट समाधान के लिए धन्यवाद! दुर्भाग्य से, यह बैश 4.3 सेगफुल करता है, जो जाल पुनरावृत्ति की अनुमति देता है। मैं 4.3.30(1)-releaseOSX पर इस पर भाग गया , और यह भी Ubuntu पर पुष्टि की है । वहाँ एक obvoius wokaround है , हालांकि :)
skozin

4
मैं काफी नहीं समझता -$$। यह '- <PID> `जैसे मूल्यांकन करता है -1234। किल मैनपेज // बिलियन मैनपेज में एक प्रमुख डैश भेजे जाने वाले सिग्नल को निर्दिष्ट करता है। हालांकि - शायद वह ब्लॉक करता है, लेकिन फिर प्रमुख डैश अन्यथा अनिर्धारित है। कोई मदद?
इवान बेने

4
@ इवानबेन्: चेक man 2 kill, जो बताता है कि जब एक पीआईडी ​​नकारात्मक है, तो प्रक्रिया समूह में सभी प्रक्रियाओं को प्रदान की गई आईडी ( en.wikipedia.org/wiki/Process_group ) के साथ सिग्नल भेजा जाता है । यह भ्रामक है कि इस में उल्लेख नहीं है man 1 killया man bash, और प्रलेखन में एक बग माना जा सकता है।
user001

111

अपडेट: https://stackoverflow.com/a/53714583/302079 इसमें एग्जिट स्टेटस और क्लीनअप फंक्शन जोड़कर सुधार करता है।

trap "exit" INT TERM
trap "kill 0" EXIT

क्यों कन्वर्ट INTऔर TERMबाहर निकलने के लिए? क्योंकि दोनों को kill 0एक अनंत लूप में प्रवेश किए बिना ट्रिगर करना चाहिए ।

क्यों ट्रिगर kill 0पर EXIT? क्योंकि सामान्य स्क्रिप्ट निकास को ट्रिगर होना चाहिए kill 0, भी।

क्यों kill 0? क्योंकि नेस्टेड सबशेल्स को भी मारने की जरूरत है। यह पूरी प्रक्रिया के पेड़ को नीचे ले जाएगा ।


3
डेबियन पर मेरे मामले का एकमात्र समाधान।
माइंडलेसरंगर

3
न तो जोहानस शाउब द्वारा जवाब दिया गया और न ही टोकेलैंड द्वारा प्रदान की गई पृष्ठभूमि की प्रक्रियाओं को मारने में कामयाब रहा, जो मेरी शेल स्क्रिप्ट शुरू हुई (रॉबर्ट पर)। यह समाधान काम कर गया। मुझे नहीं पता कि यह जवाब क्यों नहीं दिया गया है। क्या आप इसके बारे में अधिक विस्तार कर सकते हैं कि वास्तव में kill 0इसका क्या मतलब है / करता है?
जॉस

7
यह भयानक है, लेकिन मेरे माता-पिता के खोल को भी मारता है :-(
vidstige

5
यह समाधान वस्तुतः ओवरकिल है। किल 0 (मेरी स्क्रिप्ट के अंदर) ने मेरा पूरा एक्स सेशन बर्बाद कर दिया! शायद कुछ मामलों में 0 को मारना उपयोगी हो सकता है, लेकिन यह इस तथ्य को नहीं बदलता है कि यह सामान्य समाधान नहीं है और यदि संभव हो तो इसे टाला जाना चाहिए जब तक कि इसका उपयोग करने के लिए बहुत अच्छा कारण न हो। एक चेतावनी जोड़ना अच्छा होगा कि यह मूल स्क्रिप्ट या पूरे एक्स सत्र को मार सकता है, न कि केवल एक स्क्रिप्ट की पृष्ठभूमि की नौकरियां!
Lissanro Rayen

3
हालांकि यह कुछ परिस्थितियों में एक दिलचस्प समाधान हो सकता है, जैसा कि @vidstige द्वारा बताया गया है यह पूरी प्रक्रिया समूह को मार देगा जिसमें लॉन्चिंग प्रक्रिया (ज्यादातर मामलों में मूल शेल) शामिल है। निश्चित रूप से कुछ ऐसा नहीं है जो आप चाहते हैं जब आप एक आईडीई के माध्यम से स्क्रिप्ट चला रहे हों।
मैटपेन

21

जाल 'मार $ (नौकरियों -p)' बाहर निकलें

मैं जोहान्स के उत्तर में केवल मामूली बदलाव करूंगा और नौकरियों का उपयोग करूंगा-मार को रनिंग प्रक्रियाओं तक सीमित कर दूंगा और सूची में कुछ और सिग्नल जोड़ दूंगा:

trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT

रुकी हुई नौकरियों को भी क्यों नहीं मारते? Bash EXIT जाल में SIGINT और SIGTERM के मामले में भी चलाया जाएगा, इसलिए इस तरह के संकेत के मामले में जाल को दो बार बुलाया जाएगा।
जर्नो

14

trap 'kill 0' SIGINT SIGTERM EXITसमाधान में वर्णित @ tokland का जवाब वास्तव में अच्छा है, लेकिन नवीनतम बैश एक segmantation गलती के साथ दुर्घटनाओं जब इसे का उपयोग। ऐसा इसलिए है क्योंकि बैश, v। 4.3 से शुरू होकर, ट्रैप पुनरावृत्ति की अनुमति देता है, जो इस मामले में अनंत हो जाता है:

  1. खोल प्रक्रिया प्राप्त करता है SIGINTया SIGTERMया EXIT;
  2. सिग्नल फंस जाता है, निष्पादित हो जाता है kill 0, जो SIGTERMसमूह में सभी प्रक्रियाओं को भेजता है, जिसमें स्वयं शेल भी शामिल है;
  3. 1 पर जाएं :)

यह जाल को मैन्युअल रूप से डी-रजिस्टर करके चारों ओर काम किया जा सकता है:

trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT

अधिक फैंसी तरीका है, जो हटाए गए सिग्नल को प्रिंट करने और "समाप्त:" संदेश से बचने की अनुमति देता है:

#!/usr/bin/env bash

trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
  local func="$1"; shift
  for sig in "$@"; do
    trap "$func $sig" "$sig"
  done
}

stop() {
  trap - SIGINT EXIT
  printf '\n%s\n' "recieved $1, killing children"
  kill -s SIGINT 0
}

trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP

{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &

while true; do read; done

UPD : न्यूनतम उदाहरण जोड़ा गया; stopअनावश्यक संकेतों को हटाने और "समाप्त:" संदेशों को आउटपुट से छिपाने के लिए एवियोड डी-ट्रैपिंग में सुधार हुआ । सुझाव के लिए धन्यवाद ट्रेवर बॉयड स्मिथ !


में stop()आप सिग्नल नंबर के रूप में पहला तर्क प्रदान करते हैं लेकिन फिर आप हार्डकोड करते हैं कि सिग्नल किस तरह से निष्क्रिय किए जा रहे हैं। हार्डकोड के बजाय सिग्नल डीरेगिस्टर किए जा रहे हैं आप stop()फ़ंक्शन में डेरेगिस्टर के लिए पहले तर्क का उपयोग कर सकते हैं (ऐसा करने से संभावित रूप से अन्य पुनरावर्ती सिग्नल (3 हार्डकोड के अलावा) बंद हो जाएंगे)।
ट्रेवर बॉयड स्मिथ

@TrevorBoydSmith, यह उम्मीद के मुताबिक काम नहीं करेगा, मुझे लगता है। उदाहरण के लिए, शेल को मारा जा सकता है SIGINT, लेकिन kill 0भेजता है SIGTERM, जो एक बार फिर से फंस जाएगा। यह अनंत पुनरावृत्ति उत्पन्न नहीं करेगा, हालांकि, क्योंकि SIGTERMदूसरी stopकॉल के दौरान डी-फंस जाएगा ।
स्कोजिन

शायद, trap - $1 && kill -s $1 0बेहतर काम करना चाहिए। मैं इस उत्तर का परीक्षण और अद्यतन करूंगा। अच्छे विचार के लिए धन्यवाद! :)
स्कोज़िन

नहीं, trap - $1 && kill -s $1 0हम भी नहीं मार सकते हैं, क्योंकि हम साथ काम नहीं कर सकते EXIT। लेकिन यह वास्तव में पर्याप्त है डी-ट्रैप TERM, क्योंकि killयह संकेत डिफ़ॉल्ट रूप से भेजता है।
skozin

मैंने पुनरावृत्ति का परीक्षण किया EXIT, trapसिग्नल-हैंडलर को केवल एक बार निष्पादित किया जाता है।
ट्रेवर बॉयड स्मिथ

9

सुरक्षित पक्ष पर होने के लिए मुझे एक सफाई फ़ंक्शन को परिभाषित करना और जाल से कॉल करना बेहतर लगता है:

cleanup() {
        local pids=$(jobs -pr)
        [ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]

या पूरी तरह से समारोह से परहेज:

trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]

क्यों? क्योंकि बस का उपयोग करके trap 'kill $(jobs -pr)' [...]एक मानता है कि वहाँ होगा जब जाल हालत का संकेत है पृष्ठभूमि में चल रहे रोजगार के अवसर हो। जब कोई काम नहीं होता है, तो निम्न (या समान) संदेश देखेंगे:

kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

क्योंकि jobs -prखाली है - मैं उस 'जाल' (सजा का इरादा) में समाप्त हो गया।


यह टेस्ट केस [ -n "$(jobs -pr)" ]मेरे बैश पर काम नहीं करता है। मैं GNU बैश, संस्करण 4.2.46 (2) -release (x86_64-redhat-linux-gnu) का उपयोग करता हूं। "मार: उपयोग" संदेश पॉप अप करता रहता है।
डौवे वैन डेर लेएस्ट

मुझे संदेह है कि jobs -prयह इस तथ्य के साथ करना है कि पृष्ठभूमि प्रक्रियाओं के बच्चों के पीआईडी ​​वापस नहीं करते हैं। यह पूरी प्रक्रिया के पेड़ को नहीं फाड़ता है, केवल जड़ों को काटता है।
डौवे वैन डेर लेएस्ट

2

लिनक्स, बीएसडी और मैकओएस एक्स के तहत काम करने वाला एक अच्छा संस्करण। पहले SIGTERM भेजने की कोशिश करता है, और अगर यह सफल नहीं होता है, तो 10 सेकंड के बाद प्रक्रिया को मारता है।

KillJobs() {
    for job in $(jobs -p); do
            kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &)

    done
}

TrapQuit() {
    # Whatever you need to clean here
    KillJobs
}

trap TrapQuit EXIT

कृपया ध्यान दें कि नौकरियों में भव्य बच्चों की प्रक्रिया शामिल नहीं है।



1

एक अन्य विकल्प यह है कि स्क्रिप्ट को स्वयं प्रक्रिया समूह के नेता के रूप में सेट किया जाए, और बाहर निकलने पर आपके प्रक्रिया समूह पर एक हत्यारे को फंसा दिया जाए।


आप प्रक्रिया को समूह के नेता के रूप में कैसे सेट करते हैं? "Killpg" क्या है?
जर्नो

0

इसलिए स्क्रिप्ट को लोड करने की स्क्रिप्ट करें। killallस्क्रिप्ट समाप्त होते ही (या आपके OS पर जो कुछ भी उपलब्ध है) कमांड चलाएं ।


0

जॉब्स -p सभी शेल में काम नहीं करता है यदि सब-शेल में कहा जाता है, संभवतः तब तक जब तक इसका आउटपुट किसी फ़ाइल में रीडायरेक्ट न हो जाए लेकिन पाइप न हो। (मुझे लगता है कि यह मूल रूप से केवल इंटरैक्टिव उपयोग के लिए इरादा था।)

निम्नलिखित के बारे में क्या:

trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]

डेबियन के डैश शेल के साथ "नौकरियों" के लिए कॉल की आवश्यकता होती है, जो कि लापता होने पर वर्तमान नौकरी ("%%") को अपडेट करने में विफल रहता है।


हम्म दिलचस्प दृष्टिकोण, लेकिन यह काम नहीं करता है। स्कोरिंग पर विचार करें trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; doneयदि आप इसे बाश (5.0.3) में चलाते हैं और समाप्त करने की कोशिश करते हैं, तो एक अनंत लूप लगता है। हालांकि, यदि आप इसे फिर से समाप्त करते हैं, तो यह काम करता है। यहां तक ​​कि डैश (0.5.10.2-6) तक आपको इसे दो बार समाप्त करना होगा।
जारो

0

जब मैंने http://veithen.github.io/2014/11/16/sigterm-propagation.html से ज्ञान के साथ संयुक्त @ tokland के उत्तर का रूपांतरण किया, तो मैंने देखा कि trapअगर मैं अग्रभूमि प्रक्रिया चला रहा हूं तो ट्रिगर नहीं होता है (पृष्ठभूमि के साथ नहीं &):

#!/bin/bash

# killable-shell.sh: Kills itself and all children (the whole process group) when killed.
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered.
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT

echo $@
"$@" &
PID=$!
wait $PID
trap - SIGINT SIGTERM EXIT
wait $PID

यह काम करने का उदाहरण:

$ bash killable-shell.sh sleep 100
sleep 100
^Z
[1]  + 31568 suspended  bash killable-shell.sh sleep 100

$ ps aux | grep "sleep"
niklas   31568  0.0  0.0  19640  1440 pts/18   T    01:30   0:00 bash killable-shell.sh sleep 100
niklas   31569  0.0  0.0  14404   616 pts/18   T    01:30   0:00 sleep 100
niklas   31605  0.0  0.0  18956   936 pts/18   S+   01:30   0:00 grep --color=auto sleep

$ bg
[1]  + 31568 continued  bash killable-shell.sh sleep 100

$ kill 31568
Caught SIGTERM, sending SIGTERM to process group
[1]  + 31568 terminated  bash killable-shell.sh sleep 100

$ ps aux | grep "sleep"
niklas   31717  0.0  0.0  18956   936 pts/18   S+   01:31   0:00 grep --color=auto sleep

0

विविधता के लिए मैं https://stackoverflow.com/a/2173421/102484 की विविधता पोस्ट करूंगा , क्योंकि उस समाधान से मेरे वातावरण में "टर्मिनेटेड" संदेश जाता है:

trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.