- जब Bash स्क्रिप्ट उम्मीद के मुताबिक काम नहीं करती, तो चल रहे कमांड्स को जैसा है वैसा देखना भर से भी कारण ढूंढना आसान हो जाता है
set -xहर लाइन को variable expansion के बाद प्रिंट करता है, जिससे पता चलता है कि स्क्रिप्ट वास्तव में कौन-से कमांड चला रही है- कमांड लाइन से
bash -x script.shचलाने परscript.shकी शुरुआत मेंset -xजोड़ने जैसा ही असर होता है DEBUGtrap औरreadको साथ में इस्तेमाल करने पर हर लाइन चलने से पहले रुककर फ़ाइल नाम, लाइन नंबर और अगला कमांड देखा जा सकता हैdie() { echo $1 >&2; exit 1; }फ़ंक्शन किसी असफल कमांड के बाद जोड़कर standard error message दिखाने के बाद बाहर निकलने के flow को सरल बनाता है
execution flow को आँखों से देखना
set -xस्क्रिप्ट द्वारा चलाई जा रही लाइनों को प्रिंट करता है, और variables को expanded values के साथ दिखाता है- इसे स्क्रिप्ट की शुरुआत में
set -xडालकर इस्तेमाल किया जा सकता है - यही काम कमांड लाइन से भी किया जा सकता है
$ bash -x script.sh- यह
script.shके सबसे ऊपरset -xजोड़ने के बराबर है
हर लाइन पर रुककर देखना
DEBUGtrap हर code line के execute होने से पहले चलता है- अगर स्क्रिप्ट की शुरुआत में नीचे दिया गया code डालें, तो अगला कमांड चलाने से पहले Enter input का इंतज़ार होगा
trap '(read -p "\[$BASH_SOURCE: $LINENO] $BASH_COMMAND")' DEBUGread -pmessage दिखाता है और Enter input का इंतज़ार करता है$BASH_SOURCEस्क्रिप्ट फ़ाइल का नाम है$LINENOline number है$BASH_COMMANDअगला चलने वाला command है
failure पर message देकर बाहर निकलना
dieफ़ंक्शन का उपयोग command failure होने पर message प्रिंट करके program बंद करने के लिए किया जा सकता हैdie() { echo $1 >&2; exit 1; }- किसी fail हो सकने वाले command के बाद
some_command || die "oh no!"की तरह जोड़ सकते हैं
- यह फ़ंक्शन message को standard error पर भेजता है और
exit 1के साथ बंद हो जाता है
1 टिप्पणियां
Hacker News की राय
कोड में कई जगह
zdebuglogging function डाला गया है: https://github.com/zbm-dev/zfsbootmenu/blob/master/zfsbootme...debug logging चालू होने पर main menu में Ctrl-T दबाने से ऐसा स्क्रीन दिखता है: https://i.imgur.com/Ge75zkP.png
साथ ही https://github.com/zbm-dev/zfsbootmenu/blob/master/zfsbootme... से चालू की जा सकने वाली flamegraph profiling भी है, और serial port पर dump किए गए डेटा को फिर से जोड़कर https://raw.githubusercontent.com/zbm-dev/zfsbootmenu/master... जैसा ग्राफ बनाया जा सकता है
Bash उम्मीद से ज़्यादा flexible है
set -xइस्तेमाल करते समयPS4='+ ${BASH_SOURCE:-}:${FUNCNAME[0]:-}:L${LINENO:-}: 'बहुत काम का हैइससे filename, function name, line number दिखते हैं, इसलिए बड़े Bash scripts को debug करते समय काफ़ी मदद मिलती है
shellcheckकी भी सिफारिश है। यह भले सीधे समस्या न पकड़ पाए, लेकिन संभावित समस्याओं की ओर इशारा करता हैऔर script को किसी दूसरी language में फिर से लिखने की भी सलाह है। कंपनी में Bash scripts को Rust में बदला जा रहा है; शुरुआती लागत ज़्यादा है, लेकिन नतीजे में code काफ़ी ज़्यादा maintainable और reliable होता है
तेज़ scripts के लिए Bash अब भी अच्छा है, लेकिन 100 lines के आसपास पहुँचते ही ऐसी language लेना ठीक है जो मज़बूत guarantees दे सके
die()एक helper function है जो error message को standard error पर प्रिंट करके दिए गए error code के साथ exit करता हैऔर ज़्यादा shell script exit codes और helper functions यहाँ हैं: https://github.com/SixArm/unix-shell-script-kit/blob/main/un...
लेकिन उस
dieकी philosophy को लेकर थोड़ी आपत्ति है।diefunction को मूल रूप से failed command का exit code पास करना चाहिए, और उस command का error output भी छिपाना नहीं चाहिएअगर बड़े script में command failure को मैं खुद कोई अर्थ देना चाहूँ, तो उसके लिए अलग, ज़्यादा specific
dieइस्तेमाल करूँगा। मेराdieलगभग__errex "$?" "${LINENO}" "$0"जैसा है, जो fatal error, line number, script name और message प्रिंट करके उसी exit code के साथ खत्म करता हैइसका एक implementation example यहाँ है: https://github.com/TritonDataCenter/sdc-headnode/blob/master...
some-command || fail "message"की तरह इस्तेमाल करने पर, जबsome-commandnon-zero exit status लौटाता है, तो यह stack trace बनाता है और shell को बंद कर देता हैअगर function के भीतर stack trace बनाकर return करना हो, तो
some-command || softfail "message" || return $?की तरह इस्तेमाल किया जा सकता हैज़रूरी काम तो हो जाता है, लेकिन यह भद्दी है और इसका syntax भी भयानक है। जब script एक खास size या complexity तक पहुँचती है, तो यह आपको किसी proper language में जाने को मजबूर कर देती है; शायद यह किसी तरह का intentional design भी हो सकता है
modern distributions में आम तौर पर Bash का काफ़ी नया version होता है, और अगर arrays जैसी चीज़ें इस्तेमाल नहीं करनी हैं, तो version की ज़्यादा चिंता भी नहीं करनी पड़ती
Bash की ताकत दूसरी languages और tools के बीच उसकी जगह में है। यह दूसरे tools को जोड़ने के लिए ideal है, operating system के काफ़ी क़रीब है इसलिए सुविधाजनक है, और Python की तरह library installation की ज़रूरत भी नहीं पड़ती
अक्सर कहा जाता है कि ज़्यादा complex scripting को Python जैसी language में ले जाओ, लेकिन लंबी अवधि में इससे complexity की एक अतिरिक्त परत जुड़ सकती है जो मददगार न हो। 20 साल पहले लिखे Bash scripts आज भी ठीक चलते हैं, लेकिन 20 साल पुराने Python programs में version problems होने की संभावना काफ़ी ज़्यादा है
Plan 9 का
rcथोड़ा ज़्यादा साफ़-सुथरा है, लेकिन “लगभग वही, बस ज़्यादा साफ़” होने से कोई switch नहीं करता। आप अभी https://pkgsrc.se/shells से इसी तरह की लेकिन बेहतर चीज़ install कर सकते हैं, फिर भी लोग इस्तेमाल नहीं करते, और दूसरे लोगों के लिए इसे चलाने का तरीका भी नहीं बदलताकिसी स्थापित technology को replace करने के लिए किसी चीज़ का core aspects में कई गुना बेहतर होना ज़रूरी है। Plan 9 भी UNIX परिवार से बेहतर था, लेकिन replace करने लायक पर्याप्त बेहतर नहीं था
Bourne shell scripting की niche को replace करने लायक कुछ बनाना मुश्किल है। उससे पहले ही वह Perl, Python, Ruby जैसी असली scripting languages के ecological position या problem space में पहुँच जाता है
संकीर्ण problem spaces में local optimum सारी हवा खींच लेता है, जिससे सैद्धांतिक global optimum के क़रीब कोई competitor उभरना मुश्किल हो जाता है
हाल में जो अतिरिक्त फीचर्स जुड़े हैं, वे
sh/Bash के ऊपर ठीक से बैठ जाते हैं, लेकिन आखिरकार shell scripting एक मकसद को पूरा करने का साधन है और इसे सामान्य programming language की तुलना में कहीं अधिक धीरे evolve होना चाहिएBash/
shकी मुख्य खासियत यह है कि यह anti-entropic है। इसमें development या evolution लगभग नहीं के बराबर होता है, इसलिए dependency या नए features की वजह से सिरदर्द होने की संभावना कम रहती है, और जो चीज़ें 20 साल पहले चलती थीं वे आज भी बुनियादी tools बनी रहती हैंइसके design के कारण यह बदलाव का विरोध करने वाला system बन जाता है, और जब लोग इसकी सीमाओं से टकराते हैं तो उन्हें इसके बाहर जाने की प्रेरणा मिलती है
FreeBSD की ज़्यादातर scripts
shके लिए लिखी जाती हैं, औरshPOSIX standard का हिस्सा है, इसलिए मुझे लगता है कि उसका support कहीं ज़्यादा व्यापक है। Bash बस लोकप्रिय है, ऐसा मानता हूँलेकिन Bash इतना खराब था कि Groovy scripts इस्तेमाल करने के लिए मैंने namespace कम किए हुए utilities का एक ढेर बना लिया था। उन्हें IDE में develop किया जा सकता था, library system भी सुरक्षित था, और Groovy ने Java की लगभग सारी असुविधाएँ काफी हद तक सुधार दी थीं, इसलिए वह कहीं बेहतर था
इसकी कुछ सीमाएँ हैं, लेकिन आम तौर पर उपयोगी हो सकता है: https://github.com/ketancmaheshwari/pd
die()तकनीक अच्छी है, लेकिन Bash में एक परेशान करने वाली विशेषता है। अगर आप subshell के अंदरexitकरने की कोशिश करते हैं, तो सिर्फ subshell बंद होता है और बाकी script चलती रहती हैउदाहरण के लिए, अगर
cat myfile | while read line; do ... die "Found match" ... doneजैसी pipeline के भीतरdieको call किया जाए, तो बाद काecho "I don't want this line"फिर भी print हो जाएगाकई मामलों में subshell से बचा जा सकता है, और इस उदाहरण में
shellcheckका UUOC बताना सही है; उसे ठीक कर देने से subshell के अंदरdieवाली समस्या भी सुलझ जाती हैलेकिन कभी-कभी subshell से बचना संभव नहीं होता, या उससे बचने के लिए script बहुत जटिल हो जाती है। ऐसे में script की शुरुआत में
MYPID=$$से PID पकड़कर रखा जा सकता है औरdie() { echo "$1" >&2; kill -9 $MYPID; exit 1; }जैसे तरीके से process को मारा जा सकता हैबेशक, इसमें भी trade-off है। इस तरह मारने का तरीका काफ़ी rough है, और पता नहीं क्यों, यह पूरी तरह भरोसेमंद भी नहीं लगा
set -eजोड़ देने से भी, जब subshell non-zero error code के साथ बंद होता है, script भी साथ में बंद हो जाती हैसमझ नहीं आता कि किसी भी shell script में
set -eन रखने की वजह क्या हो सकती हैset -euxo pipefailडालता हूँइससे condition tests थोड़े मुश्किल हो जाते हैं, लेकिन खासकर
pipefailने अकेले ही कई बार बहुत काम किया हैset +xसे बंद भी किया जा सकता हैइसे लगातार चालू छोड़ दें तो यह काफ़ी उबाऊ हो जाता है
हाँ,
-xको मैं तब तक बचाकर रखता हूँ जब तक सच में सारी गंदी debug output देखने की ज़रूरत न पड़े