मैं बैश लिपि से कीस्ट्रोके से बुलाए गए टाइमआउट को क्यों नहीं मार सकता?


11

[संपादित करें: यह कुछ अन्य प्रश्नों के समान है जो पूछते हैं कि सभी स्पैन्ड प्रक्रियाओं को कैसे मारना है - उत्तर सभी को pkill का उपयोग करते प्रतीत होते हैं। इसलिए मेरे सवाल का मूल यह हो सकता है: क्या स्क्रिप्ट द्वारा प्रायोजित सभी प्रक्रियाओं के लिए Ctrl-C / Z को प्रचारित करने का कोई तरीका है?]

जब कोरुटिल्स ( यहां चर्चा की गई ) से कमांड के recसाथ एक सॉक्स को कॉल करते हैं , तो बैश स्क्रिप्ट के भीतर से इसे मंगाने के बाद कीस्ट्रोके के साथ इसे मारने का कोई तरीका नहीं लगता है।timeout

उदाहरण:

timeout 10 rec test.wav

... मार के साथ Ctrl+ Cया Ctrl+ Zसे मारा जा सकता है , लेकिन नहीं जब यह एक स्क्रिप्ट के अंदर से बुलाया गया है।

timeout 10 ping nowhere

... के साथ मार डाला जा सकता है Ctrl+ Cया Ctrl+ Zबैश से, और साथ Ctrl+ Zजब यह एक स्क्रिप्ट के भीतर से चला रहा है।

मैं प्रक्रिया आईडी पा सकता हूं और इसे इस तरह से मार सकता हूं, लेकिन मैं मानक ब्रेक कीस्ट्रोक का उपयोग क्यों नहीं कर सकता हूं? और क्या मेरी स्क्रिप्ट को तैयार करने का कोई तरीका है ताकि मैं कर सकूं?


2
Ctrl + Z प्रक्रियाओं को नहीं मारता है, केवल उन्हें रोक देता है। वे को चालू रखने के लिए यदि आप दे देंगे bgके fgआदेशों। वैसे भी, क्या आपके 1 और 3 डी उदाहरणों के बीच अंतर है?
terdon

मेरे पास timeoutअपने सिस्टम पर नहीं है, लेकिन sleepकाम करता है कि क्या यह कमांड लाइन पर सीधे टाइप किया गया है, खट्टा, निष्पादित या दुभाषिया के माध्यम से स्पष्ट रूप से पारित किया गया है
केविन

@terdon धन्यवाद, मैंने उदाहरणों को स्पष्ट किया है।
मीटार

जवाबों:


19

सिग्नल कुंजियाँ जैसे Ctrl+ Cअग्रभूमि प्रक्रिया समूह में सभी प्रक्रियाओं के लिए एक संकेत भेजती हैं ।

विशिष्ट मामले में, एक प्रक्रिया समूह एक पाइपलाइन है। उदाहरण के लिए, इन head <somefile | sortप्रोसेस रनिंग headऔर प्रोसेस रनिंग sortएक ही प्रोसेस ग्रुप में हैं, जैसा कि शेल है, इसलिए वे सभी सिग्नल प्राप्त करते हैं। जब आप बैकग्राउंड में जॉब चलाते हैं ( somecommand &), तो यह जॉब अपने प्रोसेस ग्रुप में होती है, इसलिए इसे दबाने पर Ctrl+ Cप्रभावित नहीं होता है।

timeoutकार्यक्रम का अपना प्रक्रिया समूह में ही देता है। स्रोत कोड से:

/* Ensure we're in our own group so all subprocesses can be killed.
   Note we don't just put the child in a separate group as
   then we would need to worry about foreground and background groups
   and propagating signals between them.  */
setpgid (0, 0);

जब कोई समय समाप्त होता है, timeoutतो उस प्रक्रिया समूह को मारने के सरल समीक्षक के माध्यम से जाता है जिसमें यह एक सदस्य है। चूंकि इसने खुद को एक अलग प्रक्रिया समूह में रखा है, इसलिए इसकी मूल प्रक्रिया समूह में नहीं होगी। यहां एक प्रक्रिया समूह का उपयोग करना सुनिश्चित करता है कि यदि बच्चा कई प्रक्रियाओं में आवेदन करता है, तो उसकी सभी प्रक्रियाओं को संकेत प्राप्त होगा।

जब आप timeoutसीधे कमांड लाइन पर दौड़ते हैं और Ctrl+ दबाते हैं C, तो परिणामस्वरूप SIGINT को timeoutचाइल्ड प्रोसेस द्वारा और दोनों के द्वारा प्राप्त किया जाता है, लेकिन इंटरेक्टिव शेल द्वारा नहीं जो timeoutमाता-पिता की प्रक्रिया है। जब timeoutकिसी स्क्रिप्ट से कॉल किया जाता है, तो केवल स्क्रिप्ट चलाने वाला शेल सिग्नल प्राप्त करता है: timeoutइसे प्राप्त नहीं करता है क्योंकि यह एक अलग प्रक्रिया समूह में है।

आप trapबिलिन के साथ शेल स्क्रिप्ट में एक सिग्नल हैंडलर सेट कर सकते हैं । दुर्भाग्य से, यह इतना आसान नहीं है। इस पर विचार करो:

#!/bin/sh
trap 'echo Interrupted at $(date)' INT
date
timeout 5 sleep 10
date

यदि आप 2 सेकंड के बाद Ctrl+ दबाते हैं C, तो यह अभी भी पूरे 5 सेकंड इंतजार करता है, तो "बाधित" संदेश प्रिंट करें। ऐसा इसलिए है क्योंकि शेल जाल कोड चलाने से मना करता है जबकि एक अग्रभूमि नौकरी सक्रिय है।

इसे मापने के लिए, पृष्ठभूमि में कार्य चलाएँ। सिग्नल हैंडलर में, killसिग्नल को timeoutप्रोसेस ग्रुप में रिले करने के लिए कॉल करें ।

#!/bin/sh
trap 'kill -INT -$pid' INT
timeout 5 sleep 10 &
pid=$!
wait $pid

बहुत डरपोक - खूबसूरती से काम किया! मैं अब बहुत चालाक हूँ, मर्सी!
मीटार

14

गिल्स द्वारा प्रदान किए गए उत्कृष्ट उत्तर पर निर्माण। टाइमआउट कमांड में एक अग्रभूमि विकल्प होता है, यदि उपयोग किया जाता है तो एक CTRL + C टाइमआउट कमांड से बाहर निकल जाएगा।

#!/bin/sh
trap 'echo caught interrupt and exiting;exit' INT
date
timeout --foreground 5 sleep 10
date

यह एक महान जवाब है - स्वीकृत उत्तर की तुलना में सरल और मेरे लिए ठीक काम किया है, timeoutGNU कोरुटिल्स से।
रिचवेल

1

मूल रूप से Ctrl+ Cएक SIGINTसंकेत भेजता है , जबकि Ctrl+ Z एक संकेत भेजता है SIGTSTP

SIGTSTPबस इस प्रक्रिया को रोकता है, एक SIGCONTइच्छा इसे जारी रखेगा।

यह अग्रभूमि-प्रक्रिया पर काम करता है जिसे कमांड-लाइन पर कांटा गया था।

यदि आपकी प्रक्रिया एक पृष्ठभूमि-प्रक्रिया है, तो आपको उस सिग्नल को प्रक्रियाओं को अलग तरीके से भेजना होगा। killवह करेगा। सिद्धांत रूप में एक "-" - उस किल पर ऑपरेटर को बाल प्रक्रियाओं को भी संकेत देना चाहिए लेकिन यह शायद ही कभी उम्मीद के मुताबिक काम करता है।

आगे पढ़ने के लिए: यूनिक्स-सिग्नल


यह सच है, कम से कम "उम्मीद के मुताबिक काम करता है" (जो आपकी उम्मीदों पर निर्भर करता है - आपको प्रक्रिया समूहों के बारे में पढ़ना चाहिए), लेकिन पूरी तरह अप्रासंगिक। प्रश्न SIGINT और SIGTSTP के बारे में किसी भी भ्रम को धोखा नहीं देता है।
गिल्स एसओ- बुराई को रोकना '

0

मौजूदा स्क्रिप्ट के लिए "खोज और बदलें" दृष्टिकोण प्रदान करके @Gilles के उत्तर में सुधार :

  1. इस स्निपेट को अपने कोड की शुरुआत में जोड़ें:
declare -a timeout_pids
exec 21>&1; exec 22>&2 # backup file descriptors, see https://superuser.com/a/1446738/187576
my_timeout(){
    local args tp ret
    args="$@"
    timeout $args &
    tp=$!
    echo "pid of timeout: $tp"
    timeout_pids+=($tp)
    wait $tp
    ret=$?
    count=${#timeout_pids[@]}
    for ((i = 0; i < count; i++)); do
        if [ "${timeout_pids[i]}" = "$tp" ] ; then
            unset 'timeout_pids[i]'
        fi
    done
    return $ret
}
  1. अपने INTहैंडलर में निम्न कोड जोड़ें (या मर्ज करें) :
pre_cleanup(){
    exec 1>&21; exec 2>&22 # restore file descriptors, see https://superuser.com/a/1446738/187576
    echo "Executing pre-cleanup..."
    for i in "${timeout_pids[*]}"; do
        if [[ ! -z $i ]]; then
            #echo "Killing PID: $i"
            kill -INT -$i 2> /dev/null
        fi
    done
    exit
}

trap pre_cleanup INT
  1. अपनी स्क्रिप्ट में फ़ंक्शन के timeoutसाथ कमांड बदलें my_timeout

उदाहरण

यहाँ एक उदाहरण स्क्रिप्ट है:

#!/bin/bash

# see "killing timeout": https://unix.stackexchange.com/a/57692/65781
declare -a timeout_pids
exec 21>&1; exec 22>&2 # backup file descriptors, see https://superuser.com/a/1446738/187576
my_timeout(){
    local args tp ret
    args="$@"
    timeout $args &
    tp=$!
    echo "pid of timeout: $tp"
    timeout_pids+=($tp)
    wait $tp
    ret=$?
    count=${#timeout_pids[@]}
    for ((i = 0; i < count; i++)); do
        if [ "${timeout_pids[i]}" = "$tp" ] ; then
            unset 'timeout_pids[i]'
        fi
    done
    return $ret
}

cleanup(){
    echo "-----------------------------------------"
    echo "Restoring previous routing table settings"
}

pre_cleanup(){
    exec 1>&21; exec 2>&22 # restore file descriptors, see https://superuser.com/a/1446738/187576
    echo "Executing pre-cleanup..."
    for i in "${timeout_pids[*]}"; do
        if [[ ! -z $i ]]; then
            echo "Killing PID: $i"
            kill -INT -$i 2> /dev/null
        fi
    done
    exit
}

trap pre_cleanup INT
trap cleanup EXIT

echo "Executing 5 temporary timeouts..."
unreachable_ip="192.168.44.5"
my_timeout 1s ping -c 1 "$unreachable_ip" &> /dev/null 
my_timeout 1s ping -c 1 "$unreachable_ip" &> /dev/null 
my_timeout 1s ping -c 1 "$unreachable_ip" &> /dev/null 
my_timeout 1s ping -c 1 "$unreachable_ip" &> /dev/null 
my_timeout 1s ping -c 1 "$unreachable_ip" &> /dev/null 
echo "ctrl+c now to execute cleanup"
my_timeout 9s ping -c 1 "$unreachable_ip" &> /dev/null 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.