मैं मैसेज "टेस्ट केस फेल !!!" के साथ बैश स्क्रिप्ट में एक त्रुटि उठाना चाहता हूं। बाश में यह कैसे करना है?
उदाहरण के लिए:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
मैं मैसेज "टेस्ट केस फेल !!!" के साथ बैश स्क्रिप्ट में एक त्रुटि उठाना चाहता हूं। बाश में यह कैसे करना है?
उदाहरण के लिए:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
echo you screwed up at ... | mail -s BUG $bugtrackeremailaddress
?
जवाबों:
यह उस पर निर्भर करता है जहां आप चाहते हैं कि त्रुटि संदेश संग्रहीत किया जाए।
आप निम्न कार्य कर सकते हैं:
echo "Error!" > logfile.log
exit 125
या निम्नलिखित:
echo "Error!" 1>&2
exit 64
जब आप एक अपवाद बढ़ाते हैं तो आप कार्यक्रम के निष्पादन को रोक देते हैं।
तुम भी तरह कुछ का उपयोग कर सकते exit xxx
हैं जहां xxx
त्रुटि कोड आप ऑपरेटिंग सिस्टम (0 से 255 तक) पर लौटने के लिए चाहते हो सकता है है। यहां 125
और 64
केवल यादृच्छिक कोड हैं जिनसे आप बाहर निकल सकते हैं। जब आपको ओएस को इंगित करने की आवश्यकता होती है कि प्रोग्राम असामान्य रूप से बंद हो गया (उदाहरण के लिए एक त्रुटि हुई), तो आपको गैर-शून्य निकास कोड पास करने की आवश्यकता है exit
।
जैसा कि @chepner ने बताया है , आप कर सकते हैं exit 1
, जिसका अर्थ होगा अनिर्दिष्ट त्रुटि ।
1>&2
चाल करेंगे
exit
अपने आप से सबसे हाल ही में पूरी हुई कमान के बाहर निकलने की स्थिति का उपयोग करता है, जो हो सकता है 0.
exit 1
, जो कि कन्वेंशन द्वारा एक अनिर्दिष्ट त्रुटि का मतलब है।
यदि आपका टेस्ट केस रनर असफल परीक्षणों के लिए एक गैर-शून्य कोड देता है, तो आप बस लिख सकते हैं:
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
__FILE__
, __LINE__
बैश मेंकुछ और तरीके हैं जिनकी मदद से आप इस समस्या का सामना कर सकते हैं। अपनी आवश्यकता में से एक मानकर एक शेल स्क्रिप्ट / फ़ंक्शन चलाना है जिसमें कुछ शेल कमांड हैं और जांचें कि क्या स्क्रिप्ट सफलतापूर्वक चली गई और विफलताओं के मामले में त्रुटियों को फेंक दिया।
शेल कमांड आमतौर पर बाहर निकलने वाले कोड पर भरोसा करते हैं, शेल को यह बताने के लिए लौटा देते हैं कि क्या यह कुछ अप्रत्याशित घटनाओं के कारण सफल या असफल रहा।
तो आप इन दोनों श्रेणियों पर क्या करना चाहते हैं
जिसके आधार पर आप जो करना चाहते हैं, उसका उपयोग करने के लिए शेल विकल्प उपलब्ध हैं। पहले मामले के लिए, शेल एक विकल्प प्रदान करता है 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)
यहां एक सरल जाल है जो 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
सुंदर नहीं है (इस प्रकार चीनी), और जाल को ट्रिप करने वाली अन्य चीजें थोड़ी बेवकूफ लग सकती हैं। फिर भी, मुझे यह तरीका पसंद है।
आपके पास 2 विकल्प हैं: स्क्रिप्ट के आउटपुट को किसी फ़ाइल में रीडायरेक्ट करें, स्क्रिप्ट में लॉग फ़ाइल का परिचय दें और
यहां आप मानते हैं कि स्क्रिप्ट चेतावनी और त्रुटि संदेशों सहित सभी आवश्यक जानकारी का उत्पादन करती है। फिर आप आउटपुट को अपनी पसंद की फ़ाइल पर पुनर्निर्देशित कर सकते हैं।
./runTests &> output.log
उपरोक्त आदेश मानक आउटपुट और त्रुटि आउटपुट दोनों को आपकी लॉग फ़ाइल में पुनर्निर्देशित करता है।
इस दृष्टिकोण का उपयोग करते हुए आपको स्क्रिप्ट में एक लॉग फ़ाइल शुरू करने की आवश्यकता नहीं है, और इसलिए तर्क थोड़ा आसान है।
अपनी स्क्रिप्ट में एक लॉग फ़ाइल को या तो हार्ड कोडिंग द्वारा जोड़ें:
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. का बेहतर विकल्प है। यदि आपने कई घटकों के साथ एक बड़ी स्क्रिप्ट बनाई है, तो आप प्रत्येक भाग को अलग तरीके से लॉग इन करना चाह सकते हैं और दूसरा तरीका केवल आपका है विकल्प।
मुझे अक्सर त्रुटि संदेशों को संभालने के लिए एक फ़ंक्शन लिखने के लिए उपयोगी लगता है इसलिए कोड समग्र रूप से क्लीनर है।
# 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"