एक बैश स्क्रिप्ट में त्रुटि उठाएं


104

मैं मैसेज "टेस्ट केस फेल !!!" के साथ बैश स्क्रिप्ट में एक त्रुटि उठाना चाहता हूं। बाश में यह कैसे करना है?

उदाहरण के लिए:

if [ condition ]; then
    raise error "Test cases failed !!!"
fi

1
इस त्रुटि पर आप क्या करना चाहते हैं? आपकी स्क्रिप्ट को कैसे कहा जाता है? क्या यह सिर्फ एक स्क्रिप्ट या कई स्क्रिप्ट हैं? आपकी स्क्रिप्ट के उपयोग क्या दिखने वाले हैं?
इटन रिस्नर

सिर्फ एक स्क्रिप्ट। मैंने इसे ubuntu टर्मिनल जैसे//cript/test.sh का उपयोग करके कॉल किया
नवीन कुमार



के लिए कोई प्यार नहीं echo you screwed up at ... | mail -s BUG $bugtrackeremailaddress?
infixed

जवाबों:


121

यह उस पर निर्भर करता है जहां आप चाहते हैं कि त्रुटि संदेश संग्रहीत किया जाए।

आप निम्न कार्य कर सकते हैं:

echo "Error!" > logfile.log
exit 125

या निम्नलिखित:

echo "Error!" 1>&2
exit 64

जब आप एक अपवाद बढ़ाते हैं तो आप कार्यक्रम के निष्पादन को रोक देते हैं।

तुम भी तरह कुछ का उपयोग कर सकते exit xxxहैं जहां xxxत्रुटि कोड आप ऑपरेटिंग सिस्टम (0 से 255 तक) पर लौटने के लिए चाहते हो सकता है है। यहां 125और 64केवल यादृच्छिक कोड हैं जिनसे आप बाहर निकल सकते हैं। जब आपको ओएस को इंगित करने की आवश्यकता होती है कि प्रोग्राम असामान्य रूप से बंद हो गया (उदाहरण के लिए एक त्रुटि हुई), तो आपको गैर-शून्य निकास कोड पास करने की आवश्यकता है exit

जैसा कि @chepner ने बताया है , आप कर सकते हैं exit 1, जिसका अर्थ होगा अनिर्दिष्ट त्रुटि


12
या आप इसे stderr पर भेज सकते हैं, जहाँ त्रुटियों को जाना है।

इसे stderr पर कैसे भेजें?
नवीन कुमार

2
@ user3078630, मैंने अभी अपना उत्तर संपादित किया है। 1>&2चाल करेंगे
ForceBru

यदि यह एक त्रुटि है, तो आपको गैर-शून्य निकास स्थिति के साथ भी बाहर निकलना चाहिएexitअपने आप से सबसे हाल ही में पूरी हुई कमान के बाहर निकलने की स्थिति का उपयोग करता है, जो हो सकता है 0.
chepner

3
जब तक आपके मन में कोई विशिष्ट अर्थ न हो, तब तक आपको बस उपयोग करना चाहिए exit 1, जो कि कन्वेंशन द्वारा एक अनिर्दिष्ट त्रुटि का मतलब है।
चेपनर

37

बुनियादी त्रुटि से निपटने

यदि आपका टेस्ट केस रनर असफल परीक्षणों के लिए एक गैर-शून्य कोड देता है, तो आप बस लिख सकते हैं:

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi

या इससे भी कम:

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi

या सबसे छोटा:

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }

Test_handler के निकास कोड के साथ बाहर निकलने के लिए:

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }

उन्नत त्रुटि से निपटने

यदि आप अधिक व्यापक दृष्टिकोण रखना चाहते हैं, तो आपके पास एक त्रुटि हैंडलर हो सकता है:

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}

फिर अपना परीक्षण केस चलाने के बाद इसे लागू करें:

run_test_case test_case_x
exit_if_error $? "Test case x failed"

या

run_test_case test_case_x || exit_if_error $? "Test case x failed"

इस तरह के एक त्रुटि हैंडलर होने के फायदे exit_if_errorहैं:

  • हम सभी एरर हैंडलिंग लॉजिक को लॉगर कर सकते हैं जैसे लॉगिंग , स्टैक ट्रेस प्रिंट करना , नोटिफिकेशन, सफाई करना आदि, एक ही जगह पर
  • त्रुटि हैंडलर को एक तर्क के रूप में त्रुटि कोड प्राप्त करने से, हम कॉलर को उन ifब्लॉकों के अव्यवस्था से अलग कर सकते हैं जो त्रुटियों के लिए निकास कोड का परीक्षण करते हैं
  • यदि हमारे पास सिग्नल हैंडलर ( ट्रैप का उपयोग करके ) है, तो हम वहां से त्रुटि हैंडलर को आमंत्रित कर सकते हैं

लाइब्रेरी को संभालने और लॉग करने में त्रुटि

यहाँ त्रुटि से निपटने और लॉगिंग का पूरा कार्यान्वयन है:

https://github.com/codeforester/base/blob/master/lib/stdlib.sh


संबंधित पोस्ट


9

कुछ और तरीके हैं जिनकी मदद से आप इस समस्या का सामना कर सकते हैं। अपनी आवश्यकता में से एक मानकर एक शेल स्क्रिप्ट / फ़ंक्शन चलाना है जिसमें कुछ शेल कमांड हैं और जांचें कि क्या स्क्रिप्ट सफलतापूर्वक चली गई और विफलताओं के मामले में त्रुटियों को फेंक दिया।

शेल कमांड आमतौर पर बाहर निकलने वाले कोड पर भरोसा करते हैं, शेल को यह बताने के लिए लौटा देते हैं कि क्या यह कुछ अप्रत्याशित घटनाओं के कारण सफल या असफल रहा।

तो आप इन दोनों श्रेणियों पर क्या करना चाहते हैं

  • त्रुटि पर बाहर निकलें
  • त्रुटि पर निकास और सफाई

जिसके आधार पर आप जो करना चाहते हैं, उसका उपयोग करने के लिए शेल विकल्प उपलब्ध हैं। पहले मामले के लिए, शेल एक विकल्प प्रदान करता है set -eऔर दूसरे के लिए आप एक trapऑन कर सकते हैंEXIT

क्या मुझे exitअपनी स्क्रिप्ट / फ़ंक्शन का उपयोग करना चाहिए ?

का प्रयोग exitआम तौर पर कुछ दिनचर्या में पठनीयता को बढ़ाता है, एक बार आप इस सवाल का जवाब पता है, तुम बुला दिनचर्या तुरंत करने के लिए बाहर निकलने के लिए चाहते हैं। यदि दिनचर्या को इस तरह से परिभाषित किया जाता है कि किसी त्रुटि के बारे में पता चलने के बाद उसे और अधिक सफाई की आवश्यकता नहीं होती है, तो तुरंत बाहर निकलने का मतलब यह नहीं है कि आपको अधिक कोड लिखना होगा।

तो ऐसे मामलों में यदि आपको स्क्रिप्ट की समाप्ति को साफ करने के लिए स्क्रिप्ट पर क्लीन-अप कार्रवाई करने की आवश्यकता है, तो इसका उपयोग नहीं करना पसंद किया जाता है exit

क्या मुझे set -eबाहर निकलने पर त्रुटि के लिए उपयोग करना चाहिए ?

नहीं!

set -eशेल में "स्वचालित त्रुटि का पता लगाने" को जोड़ने का एक प्रयास था। इसका लक्ष्य किसी भी समय त्रुटि होने के कारण शेल को नष्ट करना था, लेकिन यह उदाहरण के लिए कई संभावित नुकसान के साथ आता है,

  • यदि एक परीक्षण का हिस्सा हैं जो कमांड प्रतिरक्षा हैं। उदाहरण में, यदि आप उम्मीद करते हैं कि यह testगैर-मौजूदा निर्देशिका के चेक पर टूट जाएगा, तो यह दूसरी शर्त पर नहीं जाएगा

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
  • पिछले एक के अलावा एक पाइपलाइन में कमांड, प्रतिरक्षा हैं। नीचे दिए गए उदाहरण में, क्योंकि हाल ही में निष्पादित (सबसे दाएं) कमांड का निकास कोड माना जाता है ( cat) और यह सफल रहा। यह set -o pipefailविकल्प द्वारा सेटिंग से बचा जा सकता है लेकिन अभी भी एक चेतावनी है।

    set -e
    somecommand that fails | cat -
    echo survived 

उपयोग के लिए अनुशंसित - trapबाहर निकलने पर

फैसला यह है कि अगर आप आंख मूंदकर बाहर निकलने के बजाय एक त्रुटि को संभालने में सक्षम होना चाहते हैं set -e, trapतो ERRछद्म संकेत पर उपयोग करें ।

ERRजब खोल ही एक गैर शून्य त्रुटि कोड के साथ बाहर निकल जाता है जाल कोड को चलाने के लिए नहीं है, लेकिन जब कि खोल कि एक शर्त का हिस्सा (अगर में की तरह नहीं है के द्वारा किसी भी कमांड रन cmd, या cmd ||एक गैर शून्य बाहर निकलने की स्थिति के साथ) बाहर निकलता है ।

सामान्य अभ्यास है कि हम किस लाइन पर अतिरिक्त डिबग जानकारी प्रदान करने के लिए एक जाल हैंडलर को परिभाषित करते हैं और बाहर निकलने का क्या कारण है। अंतिम कमांड का एग्जिट कोड याद रखें जिसके कारण ERRसिग्नल अभी भी इस बिंदु पर उपलब्ध होगा।

cleanup() {
    exitcode=$?
    printf 'error condition hit\n' 1>&2
    printf 'exit code returned: %s\n' "$exitcode"
    printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
    printf 'command present on line: %d' "${BASH_LINENO[0]}"
    # Some more clean up code can be added here before exiting
    exit $exitcode
}

और हम इस हैंडलर का उपयोग उस पटकथा के शीर्ष पर नीचे की तरह कर रहे हैं जो विफल हो रही है

trap cleanup ERR

इसे एक सरल स्क्रिप्ट पर एक साथ रखना, जिसमें falseपंक्ति 15 शामिल है, जो जानकारी आपको मिल रही है

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15

यह trapभी संकेत पर पूरा होने पर सफाई को चलाने के लिए त्रुटि के बावजूद विकल्प प्रदान करता है (जैसे आपके शेल स्क्रिप्ट से बाहर निकलता है), संकेत पर EXIT। आप एक ही समय में कई संकेतों पर भी फंस सकते हैं। ट्रैप पर समर्थित संकेतों की सूची ट्रैप .1p - लिनक्स मैनुअल पेज पर पाई जा सकती है

ध्यान देने वाली एक और बात यह समझने की होगी कि यदि आप उप-गोले के साथ काम कर रहे हैं, तो कोई भी प्रदान किए गए तरीके काम नहीं करते हैं, इस स्थिति में, आपको अपनी त्रुटि से निपटने की आवश्यकता हो सकती है।

  • एक उप-शेल के साथ set -eकाम नहीं करेगा। falseउप खोल तक ही सीमित है और कभी नहीं माता पिता खोल करने के लिए मिल जाता है। यहां से निपटने में त्रुटि करने के लिए, अपने स्वयं के तर्क जोड़ें(false) || false

    set -e
    (false)
    echo survived
  • उसी के साथ trapभी होता है । नीचे दिए गए तर्क उपरोक्त कारणों से काम नहीं करेंगे।

    trap 'echo error' ERR
    (false)

5

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

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR

मैंने इसे परीक्षण करने के लिए एक लूप के साथ एक स्क्रिप्ट में रखा। मैं बस कुछ यादृच्छिक नंबरों पर हिट के लिए जांच करता हूं; आप वास्तविक परीक्षणों का उपयोग कर सकते हैं। यदि मुझे जमानत देने की आवश्यकता है, तो मैं उस संदेश के साथ गलत (जो जाल को ट्रिगर करता है) कहता हूं जिसे मैं फेंकना चाहता हूं।

विस्तृत कार्यक्षमता के लिए, ट्रैप कॉल को एक प्रोसेसिंग फ़ंक्शन है। आप हमेशा अपने arg ($ _) पर केस स्टेटमेंट का उपयोग कर सकते हैं यदि आपको अधिक क्लीनअप करने की आवश्यकता होती है, आदि। थोड़ी सी सैंटेट्रिक शुगर के लिए एक संस्करण को असाइन करें -

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false

while :
do x=$(( $RANDOM % 10 ))
   case "$x" in
   0) $throw "DIVISION BY ZERO" ;;
   3) $raise "MAGIC NUMBER"     ;;
   *) echo got $x               ;;
   esac
done

नमूना उत्पादन:

# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6

जाहिर है, आप कर सकते थे

runTest1 "Test1 fails" # message not used if it succeeds

डिजाइन सुधार के लिए बहुत सारे कमरे।

ड्रॉ बैक में वह तथ्य शामिल होता है जो falseसुंदर नहीं है (इस प्रकार चीनी), और जाल को ट्रिप करने वाली अन्य चीजें थोड़ी बेवकूफ लग सकती हैं। फिर भी, मुझे यह तरीका पसंद है।


4

आपके पास 2 विकल्प हैं: स्क्रिप्ट के आउटपुट को किसी फ़ाइल में रीडायरेक्ट करें, स्क्रिप्ट में लॉग फ़ाइल का परिचय दें और

  1. फ़ाइल में आउटपुट पुनर्निर्देशित करना :

यहां आप मानते हैं कि स्क्रिप्ट चेतावनी और त्रुटि संदेशों सहित सभी आवश्यक जानकारी का उत्पादन करती है। फिर आप आउटपुट को अपनी पसंद की फ़ाइल पर पुनर्निर्देशित कर सकते हैं।

./runTests &> output.log

उपरोक्त आदेश मानक आउटपुट और त्रुटि आउटपुट दोनों को आपकी लॉग फ़ाइल में पुनर्निर्देशित करता है।

इस दृष्टिकोण का उपयोग करते हुए आपको स्क्रिप्ट में एक लॉग फ़ाइल शुरू करने की आवश्यकता नहीं है, और इसलिए तर्क थोड़ा आसान है।

  1. स्क्रिप्ट में एक लॉग फ़ाइल का परिचय दें :

अपनी स्क्रिप्ट में एक लॉग फ़ाइल को या तो हार्ड कोडिंग द्वारा जोड़ें:

logFile='./path/to/log/file.log'

या इसे एक पैरामीटर से गुजारें:

logFile="${1}"  # This assumes the first parameter to the script is the log file

स्क्रिप्ट के शीर्ष पर लॉग फ़ाइल में निष्पादन के समय टाइमस्टैम्प जोड़ना एक अच्छा विचार है:

date '+%Y%-m%d-%H%M%S' >> "${logFile}"

फिर आप अपनी त्रुटि संदेशों को लॉग फ़ाइल में पुनर्निर्देशित कर सकते हैं

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
fi

यह लॉग फ़ाइल में त्रुटि को जोड़ देगा और निष्पादन जारी रखेगा। यदि आप महत्वपूर्ण त्रुटियां होने पर निष्पादन रोकना चाहते हैं, तो आप exitस्क्रिप्ट कर सकते हैं :

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
    # Clean up if needed
    exit 1;
fi

ध्यान दें कि exit 1 इंगित करता है कि एक अनिर्दिष्ट त्रुटि के कारण प्रोग्राम निष्पादन को रोक देता है। आप चाहें तो इसे कस्टमाइज़ कर सकते हैं।

इस दृष्टिकोण का उपयोग करके आप अपने लॉग को कस्टमाइज़ कर सकते हैं और आपकी स्क्रिप्ट के प्रत्येक घटक के लिए एक अलग लॉग फ़ाइल हो सकती है।


यदि आपके पास अपेक्षाकृत छोटी स्क्रिप्ट है या किसी और की स्क्रिप्ट को संशोधित किए बिना निष्पादित करना चाहते हैं, तो पहला दृष्टिकोण अधिक उपयुक्त है।

यदि आप हमेशा लॉग फ़ाइल को उसी स्थान पर रखना चाहते हैं, तो यह 2. का बेहतर विकल्प है। यदि आपने कई घटकों के साथ एक बड़ी स्क्रिप्ट बनाई है, तो आप प्रत्येक भाग को अलग तरीके से लॉग इन करना चाह सकते हैं और दूसरा तरीका केवल आपका है विकल्प।


3

मुझे अक्सर त्रुटि संदेशों को संभालने के लिए एक फ़ंक्शन लिखने के लिए उपयोगी लगता है इसलिए कोड समग्र रूप से क्लीनर है।

# Usage: die [exit_code] [error message]
die() {
  local code=$? now=$(date +%T.%N)
  if [ "$1" -ge 0 ] 2>/dev/null; then  # assume $1 is an error code if numeric
    code="$1"
    shift
  fi
  echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
  exit $code
}

यह पिछली कमांड से त्रुटि कोड लेता है और इसे पूरी स्क्रिप्ट से बाहर निकलते समय डिफ़ॉल्ट त्रुटि कोड के रूप में उपयोग करता है। यह समय को भी नोट करता है, जिसमें माइक्रोसेकंड होता है जहां समर्थित (जीएनयू की तिथि %Nनैनोसेकंड है, जिसे हम बाद में माइक्रोसेकंड में बदल देते हैं)।

यदि पहला विकल्प शून्य या एक सकारात्मक पूर्णांक है, तो यह निकास कोड बन जाता है और हम इसे विकल्पों की सूची से हटा देते हैं। तब हम मानक त्रुटि के लिए संदेश की रिपोर्ट करते हैं, स्क्रिप्ट के नाम के साथ, शब्द "ERROR", और समय (हम microseconds, या गैर-जीएनयू समय के लिए छोटा करने के लिए नैनोकैक्शंस को छोटा करने के लिए पैरामीटर विस्तार का उपयोग करते हैं, जैसे ट्रंक 12:34:56.%Nकरने के लिए 12:34:56)। ERROR शब्द के बाद एक बृहदान्त्र और स्थान जोड़ा जाता है, लेकिन केवल तब जब कोई त्रुटि संदेश दिया गया हो। अंत में, हम पहले से निर्धारित निकास कोड का उपयोग करके स्क्रिप्ट से बाहर निकलते हैं, किसी भी जाल को सामान्य रूप में ट्रिगर करते हैं।

कुछ उदाहरण (मान लें कि कोड में रहता है script.sh ):

if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"

$command |grep -q condition || die 1 "'$command' lacked 'condition'"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"

$command || die
# exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.