प्रक्रिया से बाहर निकलें प्रक्रिया के आधार पर शेल स्क्रिप्ट से बाहर निकलें


378

मेरे पास एक शेल स्क्रिप्ट है जो कई कमांड को निष्पादित करता है। यदि कोई भी आदेश गैर-शून्य निकास कोड के साथ बाहर निकलता है तो मैं शेल स्क्रिप्ट से बाहर कैसे निकलूं?


3
मैंने उत्तर दिया कि आप बैश का उपयोग कर रहे हैं, लेकिन यदि यह एक बहुत अलग शेल है तो क्या आप अपनी पोस्ट में निर्दिष्ट कर सकते हैं?
मार्टिन डब्ल्यू

कठिन विधि: $?प्रत्येक कमांड के बाद के मूल्य का परीक्षण करें । आसान विधि: अपनी बैश लिपि के शीर्ष पर set -eया रखें #!/bin/bash -e
mwfearnley

जवाबों:


494

प्रत्येक आदेश के बाद, बाहर निकलने का कोड $?चर में पाया जा सकता है ताकि आपके पास कुछ ऐसा हो:

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

आपको पाइप्ड कमांड से सावधान रहने की आवश्यकता है क्योंकि $?एकमात्र आपको पाइप में अंतिम तत्व का रिटर्न कोड देता है, कोड में:

ls -al file.ext | sed 's/^/xx: /"

यदि फ़ाइल मौजूद नहीं है, तो त्रुटि कोड वापस नहीं आएगा (क्योंकि sedपाइपलाइन का हिस्सा वास्तव में काम करता है, 0 लौटाता है)।

bashखोल वास्तव में, एक सरणी जो उस स्थिति में सहायता कर सकते हैं प्रदान करता है कि किया जा रहा है PIPESTATUS। इस सरणी में प्रत्येक पाइपलाइन घटक के लिए एक तत्व है, जिसे आप व्यक्तिगत रूप से एक्सेस कर सकते हैं जैसे ${PIPESTATUS[0]}:

pax> false | true ; echo ${PIPESTATUS[0]}
1

ध्यान दें कि यह आपको falseपूरी पाइपलाइन नहीं बल्कि कमांड का परिणाम मिल रहा है । आप पूरी सूची को प्रोसेस करने के लिए भी प्राप्त कर सकते हैं जैसा कि आप फिट देखते हैं:

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

यदि आप पाइपलाइन से सबसे बड़ा त्रुटि कोड प्राप्त करना चाहते हैं, तो आप कुछ इस तरह का उपयोग कर सकते हैं:

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

यह PIPESTATUSबदले में प्रत्येक तत्व के माध्यम से जाता है , इसे संग्रहीत करता है rcयदि यह पिछले rcमूल्य से अधिक था ।


39
पोर्टेबल कोड की सिर्फ एक पंक्ति में समान सुविधा: ls -al file.ext || exit $?([[]] पोर्टेबल नहीं है)
२०:४४ पर मार्क

19
मार्च, मुझे लगता है कि आप पाएंगे कि [[ ]]बहुत पोर्टेबल है bash, जो है क्या सवाल टैग है :-) अजीब पर्याप्त, lsमें काम नहीं करता command.comहै, तो यह नहीं पोर्टेबल या तो, दिखौवा मैं पता है, लेकिन यह तर्क का एक ही तरह का आप वर्तमान।
paxdiablo

39
मुझे पता है कि यह प्राचीन है, लेकिन यह ध्यान दिया जाना चाहिए कि आप सरणी के माध्यम से पाइप में आदेशों का निकास कोड प्राप्त कर सकते हैं PIPESTATUS(यानी, ${PIPESTATUS[0]}पहले आदेश के ${PIPESTATUS[1]}लिए, दूसरे के लिए, या ${PIPESTATUS[*]}सभी निकास प्रतिमा की सूची के लिए।
DevSolar

11
इस पर जोर देने की जरूरत है कि सुरुचिपूर्ण और मुहावरेदार शेलिंग स्क्रिप्टिंग को शायद ही कभी $?सीधे जांचने की आवश्यकता होती है। आप आमतौर पर ऐसा कुछ चाहते हैं, if ls -al file.ext; then : nothing; else exit $?; fiजो @MarcH जैसे कोर्स के बराबर है, ls -al file.ext || exit $?लेकिन अगर thenया elseक्लॉज़ कुछ अधिक जटिल हैं, तो यह अधिक बनाए रखने योग्य है।
tripleee

9
[[ $rc != 0 ]]आप एक दे देंगे 0: not foundया 1: not foundत्रुटि। इसे बदल दिया जाना चाहिए [ $rc -ne 0 ]। इसके rc=$?बाद हटाया जा सकता है और बस इस्तेमाल किया जा सकता है [ $? -ne 0 ]
कर्टिसलाईबोलिन

223

यदि आप $ के साथ काम करना चाहते हैं?, तो आपको $ के बाद से प्रत्येक कमांड के बाद इसकी जांच करनी होगी? प्रत्येक कमांड से बाहर निकलने के बाद अपडेट किया जाता है। इसका मतलब है कि यदि आप एक पाइपलाइन निष्पादित करते हैं, तो आपको केवल पाइप लाइन में अंतिम प्रक्रिया का निकास कोड मिलेगा।

एक और तरीका यह है:

set -e
set -o pipefail

यदि आप इसे शेल स्क्रिप्ट के शीर्ष पर रखते हैं, तो ऐसा लगता है कि बैश आपके लिए इस बात का ध्यान रखेगा। पिछले पोस्टर के रूप में, "सेट-ई" किसी भी साधारण कमांड पर एक त्रुटि के साथ बाहर निकलने का कारण बनेगा। "set -o pipefail" के कारण पाइपलाइन में किसी भी कमांड पर त्रुटि के साथ बाहर निकलने का कारण होगा।

इस समस्या पर थोड़ी और चर्चा के लिए यहां या यहां देखें । यहाँ सेट बेसिन पर बैश मैनुअल सेक्शन है।


6
यह वास्तव में शीर्ष उत्तर होना चाहिए: ऐसा करना बहुत आसान है, PIPESTATUSहर जगह निकास कोड का उपयोग करना और जांचना आसान है।
कैंडु

2
#!/bin/bash -eशेल स्क्रिप्ट शुरू करने का एकमात्र तरीका है। आप हमेशा चीजों का उपयोग कर सकते हैं जैसे foo || handle_error $?कि आपको वास्तव में निकास स्थितियों की जांच करने की आवश्यकता है।
डेविस हेरिंग

53

" set -e" शायद ऐसा करने का सबसे आसान तरीका है। बस अपने प्रोग्राम में किसी भी कमांड से पहले रखें।


6
यदि set -eआपकी स्क्रिप्ट में कोई भी कमांड त्रुटि स्थिति के साथ बाहर निकलती है और आपने इस त्रुटि को हैंडल नहीं किया तो @SwaroopCH आपकी स्क्रिप्ट को रद्द कर देगी।
एंड्रयू

2
set -e100% के बराबर है, set -o errexitजिसके विपरीत पूर्व को खोजा जा सकता है। आधिकारिक दस्तावेज के लिए opengroup + errexit खोजें।
मार्ख

30

यदि आप बैश में एक्ज़िट को बिना किसी पैरामीटर के कहते हैं, तो यह अंतिम कमांड का एग्ज़िट कोड लौटा देगा। यदि पिछला आदेश विफल रहता है, तो बाश के साथ संयुक्त या केवल निकास से बाहर निकलना चाहिए। लेकिन मैंने इसका परीक्षण नहीं किया है।

कमांड 1 || बाहर जाएं;
कमांड 2 || बाहर जाएं;

बैश चर $ में अंतिम कमांड के निकास कोड को भी स्टोर करेगा।


25
[ $? -eq 0 ] || exit $?; # exit for none-zero return code

3
यह नहीं होना चाहिए exit $?(कोई parens)?
malthe

21

http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. मैं के निकास कोड कैसे प्राप्त करूं cmd1मेंcmd1|cmd2

    सबसे पहले, ध्यान दें कि cmd1निकास कोड गैर-शून्य हो सकता है और अभी भी एक त्रुटि का मतलब नहीं है। यह उदाहरण के लिए होता है

    cmd | head -1

    आप एक 141 (या ksh93 के साथ 269) का निरीक्षण कर सकते हैं cmd1, लेकिन इससे बाहर निकलने की स्थिति के कारण cmdएक SIGPIPE सिग्नल द्वारा बाधित होने के कारण ऐसा किया गया head -1था।

    एक पाइपलाइन के तत्वों की निकास स्थिति जानने के लिए cmd1 | cmd2 | cmd3

    ए। zsh के साथ:

    पिपेस्टैटस विशेष सरणी में निकास कोड दिए गए हैं। cmd1एग्जिट कोड अंदर है $pipestatus[1], cmd3एग्जिट कोड इन है $pipestatus[3], ताकि $?हमेशा की तरह ही हो $pipestatus[-1]

    ख। बैश के साथ:

    निकास कोड PIPESTATUSविशेष सरणी में प्रदान किए गए हैं । cmd1एग्जिट कोड अंदर है ${PIPESTATUS[0]}, cmd3एग्जिट कोड इन है ${PIPESTATUS[2]}, ताकि $?हमेशा की तरह ही हो ${PIPESTATUS: -1}

    ...

    अधिक जानकारी के लिए निम्न लिंक देखें ।


19

बैश के लिए:

# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;

#
# ... the rest of the script goes here
#  

function catch_errors() {
   # do whatever on errors
   # 
   #
   echo "script aborted, because of errors";
   exit 0;
}

19
शायद "0 से बाहर" नहीं होना चाहिए, क्योंकि यह सफलता को इंगित करता है।
नोबार

3
exit_code = $ ?; गूंज "स्क्रिप्ट क्योंकि त्रुटियों की, निरस्त किया गया", बाहर निकलने के $ exit_code
RaSergiy

1
@ HAL9001 क्या आपके पास इसके सबूत हैं? आईबीएम प्रलेखन अन्यथा कहता है
पैट्रिक जेम्स मैकडॉगल

11

बैश में यह आसान है, बस उन्हें एक साथ &&:

command1 && command2 && command3

निर्माण होने पर आप नेस्टेड का उपयोग भी कर सकते हैं:

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi

+1 यह सबसे आसान उपाय था जिसकी मुझे तलाश थी। इसके अलावा, आप यह भी लिख सकते हैं if (! command)कि क्या आप कमांड से नॉनज़ेरो एररकोड की उम्मीद करते हैं।
बेरी

यह अनुक्रमिक आदेशों के लिए है .. क्या होगा अगर मैं उन 3 को समानांतर में लॉन्च करना चाहता हूं और सभी को मारना है अगर उनमें से कोई भी विफल हो जाता है?
वासिले सुरदु

4
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="$@" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit

2
उपयोग करने के लिए शायद ही कभी एक कारण है $*; "$@"रिक्त स्थान और वाइल्डकार्ड को संरक्षित करने के बजाय उपयोग करें ।
डेविस हेरिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.