`सेट -eu` का उपयोग करते समय EXIT और ERR जाल का सही व्यवहार


27

ईआरआर और एग्जिट जाल के साथ ( ) set -e( errexit) का उपयोग करते समय मैं कुछ अजीब व्यवहार देख रहा हूं । वे संबंधित प्रतीत होते हैं, इसलिए उन्हें एक प्रश्न में रखना उचित लगता है।set -unounset

1) set -uईआरआर ट्रैप को ट्रिगर नहीं करता है

  • कोड:

    #!/bin/bash
    trap 'echo "ERR (rc: $?)"' ERR
    set -u
    echo ${UNSET_VAR}
  • अपेक्षित: ERR जाल कहा जाता है, RC! = 0
  • वास्तविक: ERR ट्रैप को नहीं कहा जाता है, RC == 1
  • नोट: set -eपरिणाम नहीं बदलता है

2) set -euएक EXIT ट्रैप में एग्जिट कोड का उपयोग 1 के बजाय 0 है

  • कोड:

    #!/bin/bash
    trap 'echo "EXIT (rc: $?)"' EXIT
    set -eu
    echo ${UNSET_VAR}
  • अपेक्षित: EXIT जाल कहा जाता है, RC == 1
  • वास्तविक: EXIT जाल को कहा जाता है, RC == 0
  • नोट: उपयोग करते समय set +e, RC == 1. EXIT ट्रैप उचित RC तब लौटाता है जब कोई अन्य कमांड एक एरर फेंकता है।
  • संपादित करें: एक दिलचस्प टिप्पणी के साथ इस विषय पर एक एसओ पोस्ट है जो सुझाव देता है कि यह बैश संस्करण से संबंधित हो सकता है। बैश 4.3.11 के साथ इस स्निपेट का परीक्षण एक RC = 1 में करता है, इसलिए यह बेहतर है। दुर्भाग्य से सभी मेजबानों पर बैश (3.2.51 से) को अपग्रेड करना फिलहाल संभव नहीं है, इसलिए हमें कुछ अन्य समाधान के साथ आना होगा।

किसी को भी इन व्यवहारों की व्याख्या कर सकते हैं?

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


3
4 से मुझे लगता है कि bashमानक के साथ टूट गया और उपधाराओं में जाल डालना शुरू कर दिया। माना जाता है कि जाल उसी वातावरण में निष्पादित किया जाता है, जहां से वापसी हुई थी, लेकिन bashकाफी समय तक ऐसा नहीं किया।
15 अक्टूबर को सुबह

1
एक मिनट प्रतीक्षा करें - क्या आप समाधान या स्पष्टीकरण चाहते हैं? और अगर आप एक समाधान चाहते हैं, तो एक समाधान वास्तव में क्या है? क्या आप इसे घटित करना चाहते हैं? set -eऔर set -uदोनों को विशेष रूप से एक स्क्रिप्टेड शेल को मारने के लिए डिज़ाइन किया गया है । उन स्थितियों में उनका उपयोग करना जो उनके आवेदन को ट्रिगर कर सकते हैं एक स्क्रिप्टेड शेल को मार देंगे । उनके आसपास कोई ऐसा नहीं है, सिवाय उनके उपयोग के नहीं , और उन परिस्थितियों के लिए परीक्षण करने के लिए जब वे एक कोड अनुक्रम में लागू होते हैं। तो, मूल रूप से, आप अच्छे शेल-कोड लिख सकते हैं, या आप उपयोग कर सकते हैं set -eu
चाटुकार

2
असल में, मैं दोनों की तलाश कर रहा हूं, क्योंकि मुझे पर्याप्त जानकारी नहीं मिल -uपाई है कि ईआरआर ट्रैप को ट्रिगर क्यों नहीं किया जाएगा (यह एक त्रुटि है, इसलिए इसे ट्रैप को ट्रिगर नहीं करना चाहिए) या त्रुटि कोड 1 के बजाय 0 है। उत्तरार्द्ध एक बग हो सकता है जो बाद के संस्करण में पहले से ही तय हो गया है, इसलिए ऐसा है। लेकिन पहला भाग यह समझने में काफी कठिन है कि क्या आपको एहसास नहीं हुआ है कि शेल मूल्यांकन में त्रुटि (पैरामीटर विस्तार) और कमांड में वास्तविक त्रुटियां अलग-अलग चीजें हैं। समाधान के लिए, ठीक है, जैसा कि आपने सुझाव दिया था, मैं अब -euआवश्यक होने पर मैन्युअल रूप से बचने और जांचने की कोशिश कर रहा हूं ।
डीवीडी

1
@dvdsng - अच्छा है। यह तरीका है - आपको अपनी स्क्रिप्ट पोस्ट करनी चाहिए जब आप एक उत्तर के रूप में करते हैं और अपने आप को इनाम देते हैं। मैं वास्तव में उन विकल्पों को नापसंद करता हूं - वे किसी भी सुरक्षित तरीके से अपवाद से निपटने की अनुमति नहीं देते हैं।
मिकसेर

1
@dvdsng - जहां उन विकल्पों में से कोई भी उपयोगी हो सकता है, हालांकि, एक उप-संदर्भ में है। और इसलिए यह बोधगम्य है कि आप जो कुछ भी उनके लिए पहले इस्तेमाल कर रहे थे, उसे एक उप-संदर्भ में स्थानीय किया जा सकता है जैसे: (set -u; : $UNSET_VAR)और इसी तरह। इस तरह का सामान अच्छा भी हो सकता है - आप &&कभी-कभार गिर सकते हैं : (set -e; mkdir dir; cd dir; touch dirfile)यदि आपको मेरा बहाव मिलता है। यह सिर्फ इतना है कि वे नियंत्रित संदर्भ हैं - जब आप उन्हें वैश्विक विकल्पों के रूप में सेट करते हैं, तो आप नियंत्रण खो देते हैं और नियंत्रित हो जाते हैं। आमतौर पर अधिक कुशल समाधान होते हैं, हालांकि।
चाटुकार

जवाबों:


15

से man bash:

  • set -u
    • उपचार को सेट किए बिना चर और विशेष मापदंडों के अलावा अन्य मापदंडों "@"और "*"जब पैरामीटर विस्तार करने में कोई त्रुटि के रूप में। यदि विस्तार को एक अस्थिर चर या पैरामीटर पर करने का प्रयास किया जाता है, तो शेल एक त्रुटि संदेश को प्रिंट करता है, और, यदि नहीं, तो -iगैर-सक्रिय स्थिति से बाहर निकलता है।

POSIX बताता है कि, एक विस्तार त्रुटि की स्थिति में , एक गैर-संवादात्मक शेल बाहर निकल जाएगा जब विस्तार या तो एक शेल विशेष बिलिन के साथ जुड़ा हुआ है (जो कि bashनियमित रूप से किसी भी तरह से अंतर को अनदेखा करता है, और इसलिए शायद अप्रासंगिक है) या इसके अलावा कोई अन्य उपयोगिता ।

  • शेल त्रुटियों के परिणाम :
    • एक विस्तार त्रुटि वह है जो तब होती है जब शब्द विस्तार में परिभाषित शेल विस्तार किए जाते हैं (उदाहरण के लिए "${x!y}", क्योंकि !एक वैध ऑपरेटर नहीं है) ; एक कार्यान्वयन सकता है वाक्यविन्यास त्रुटियों के रूप में इन का इलाज अगर यह बजाय विस्तार के दौरान, tokenization के दौरान उन्हें पता लगाने में सक्षम है।
    • [ए] एन इंटरैक्टिव खोल बाहर निकलने के बिना मानक त्रुटि के लिए एक नैदानिक ​​संदेश लिखेंगे।

इसके अलावा man bash:

  • trap ... ERR
    • यदि कोई sigspec ERR है , तो कमांड arg को तब भी क्रियान्वित किया जाता है जब एक पाइपलाइन (जिसमें एक साधारण कमांड शामिल हो सकती है) , एक सूची, या एक कंपाउंड कमांड एक गैर-शून्य निकास स्थिति देता है, जो निम्न स्थितियों के अधीन है:
      • ERR जाल निष्पादित नहीं है, तो विफल रहा है आदेश आदेश सूची का हिस्सा तुरंत एक निम्नलिखित है whileया untilकीवर्ड ...
      • ... एक ifबयान में परीक्षण का हिस्सा ...
      • ... एक आदेश &&या ||अंतिम &&या निम्नलिखित आदेश को छोड़कर सूची में निष्पादित का एक हिस्सा ||...
      • ... किसी भी पाइपलाइन में कमांड लेकिन आखिरी ...
      • ... या यदि कमांड के वापसी मान का उपयोग करके उलटा किया जा रहा है !
    • ये वही स्थितियां हैं जो एरेक्सिट -e विकल्प द्वारा पालन की जाती हैं ।

ऊपर ध्यान दें कि ईआरआर जाल सभी कुछ अन्य कमांड के रिटर्न के मूल्यांकन के बारे में है । लेकिन जब एक विस्तार त्रुटि होती है, तो कुछ भी वापस करने के लिए कोई कमांड रन नहीं होता है। आपके उदाहरण में, echo ऐसा कभी नहीं होता है - क्योंकि जब शेल अपने तर्कों का मूल्यांकन और विस्तार करता है, तो यह एक -uएनसेट चर का सामना करता है, जिसे स्पष्ट शेल विकल्प द्वारा वर्तमान, स्क्रिप्टेड शेल से तत्काल बाहर निकलने का कारण बताया गया है।

और इसलिए EXIT ट्रैप, यदि कोई हो, निष्पादित किया जाता है, और शेल डायग्नोस्टिक संदेश और 0 से बाहर निकलने की स्थिति के साथ बाहर निकलता है - जैसा कि इसे करना चाहिए।

आरसी: 0 के लिए के रूप में , मुझे उम्मीद है कि यह किसी प्रकार का एक संस्करण विशिष्ट बग है - शायद एक ही समय में होने वाले EXIT के लिए दो ट्रिगर्स के साथ और दूसरे को बाहर निकलने का कोड (जो नहीं होना चाहिए) हो रहा है । और वैसे भी, अप-टू-डेट bashद्विआधारी के रूप में द्वारा स्थापित pacman:

bash <<\IN
    printf "shell options:\t$-\n"
    trap 'echo "EXIT (rc: $?)"' EXIT
    set -eu
    echo ${UNSET_VAR}
IN

मैंने पहली पंक्ति को जोड़ा ताकि आप देख सकें कि शेल की स्थितियाँ एक स्क्रिप्टेड शेल की हैं - यह इंटरएक्टिव नहीं है। आउटपुट है:

shell options:  hB
bash: line 4: UNSET_VAR: unbound variable
EXIT (rc: 1)

हाल के चैंज से कुछ प्रासंगिक नोट यहां दिए गए हैं :

  • एक बग फिक्स्ड जो अतुल्यकालिक आदेशों को $?सही ढंग से सेट नहीं करने का कारण बनता है।
  • एक बग फिक्स्ड , जो गलत लाइन नंबर के लिए कमांड में विस्तार त्रुटियों द्वारा उत्पन्न त्रुटि संदेश का कारण बनता है for
  • एक बग फिक्स्ड जो SIGINT और SIGQUIT को trapअतुल्यकालिक सबशेल कमांड में नहीं होने देता है।
  • इंटरएक्टिव गोले को नजरअंदाज करने वाले दूसरे और बाद में SIGINT का कारण बनने वाली बाधा से निपटने में समस्या का समाधान ।
  • शेल अब trapउन सिग्नलों के लिए हैंडलर चलाते समय सिग्नल की प्राप्ति को अवरुद्ध नहीं करता है, और अधिकांश trap हैंडलर को पुनरावर्ती रूप से चलाने की अनुमति देता है ( हैंडलर निष्पादित trapकरते समय हैंडलरtrap चलाते हैं )

मुझे लगता है कि यह अंतिम या पहला है जो सबसे अधिक प्रासंगिक है - या संभवतः दोनों का संयोजन। एक trapहैंडलर अपने स्वभाव से अतुल्यकालिक है, क्योंकि इसका पूरा काम अतुल्यकालिक संकेतों की प्रतीक्षा करना और संभालना है । और आप दो को एक साथ ट्रिगर करते हैं -euऔर $UNSET_VAR

और इसलिए शायद आपको सिर्फ अपडेट करना चाहिए, लेकिन अगर आप खुद को पसंद करते हैं, तो आप इसे पूरी तरह से एक अलग शेल के साथ करेंगे।


पैरामीटर विस्तार को अलग तरीके से कैसे नियंत्रित किया जाता है, इसकी व्याख्या के लिए धन्यवाद। यह मेरे लिए बहुत कुछ साफ कर दिया।
डीवीडी

मैं आपको इनाम इसलिए दे रहा हूं क्योंकि आपका स्पष्टीकरण सबसे अधिक मददगार था।
डीवीडी

@dvdgsng - ग्रेसिया जिज्ञासा से बाहर, क्या तुमने कभी w / तुम्हारा समाधान आया?
मिकसर्व

9

(मैं बैश 4.2.53 का उपयोग कर रहा हूं)। भाग 1 के लिए, बैश मैन पेज केवल कहता है "मानक त्रुटि के लिए एक त्रुटि संदेश लिखा जाएगा, और एक गैर-संवादात्मक शेल बाहर निकल जाएगा"। यह नहीं कहता कि एक ईआरआर ट्रैप कहा जाएगा, हालांकि मैं मानता हूं कि यह उपयोगी होगा।

व्यावहारिक होने के लिए, यदि आप वास्तव में चाहते हैं कि अपरिभाषित चर के साथ अधिक सफाई से सामना करना है, तो एक संभावित समाधान एक फ़ंक्शन के अंदर अपने अधिकांश कोड को डालना है, फिर उस फ़ंक्शन को एक उप-शेल में निष्पादित करें और रिटर्न कोड और स्टडर आउटपुट को पुनर्प्राप्त करें। यहाँ एक उदाहरण है जहाँ "cmd ()" फ़ंक्शन है:

#!/bin/bash
trap 'rc=$?; echo "ERR at line ${LINENO} (rc: $rc)"; exit $rc' ERR
trap 'rc=$?; echo "EXIT (rc: $rc)"; exit $rc' EXIT
set -u
set -E # export trap to functions

cmd(){
 echo "args=$*"
 echo ${UNSET_VAR}
 echo hello
}
oops(){
 rc=$?
 echo "$@"
 return $rc # provoke ERR trap
}

exec 3>&1 # copy stdin to use in $()
if output=$(cmd "$@" 2>&1 >&3) # collect stderr, not stdout 
then    echo ok
else    oops "fail: $output"
fi

मेरे बैश पर मुझे मिलता है

./script my stuff; echo "exit was $?"
args=my stuff
fail: ./script: line 9: UNSET_VAR: unbound variable
ERR at line 15 (rc: 1)
EXIT (rc: 1)
exit was 1

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