5 पॉइंट द्वारा GN⁺ 2023-11-25 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • ShellCheck एक ऐसा टूल है जिससे shell script की आम गलतियों को वेब पर तुरंत जांचा जा सकता है, और paste किए गए code की समस्याएं editor के नीचे output में देखी जा सकती हैं
  • लोकल environment में इसे cabal, apt, dnf, pkg, brew install आदि से install किया जा सकता है, इसलिए package manager आधारित उपयोग संभव है
  • उदाहरण sh shebang की portability warning, semantic issues, और quote handling errors जैसी उन जगहों को दिखाते हैं जो असली scripts में अक्सर छूट जाती हैं
  • यह GPLv3 लाइसेंस वाला free software है, और GitHub, Wiki, तथा प्रमुख editors के integrated linter के रूप में भी उपलब्ध है
  • इसे CodeClimate, Codacy, और CodeFactor में GitHub repositories की automatic checking के लिए इस्तेमाल किया जा सकता है, और ShellCheck खुद Haskell में लिखा गया है

shell script analysis सुविधाएं

  • ShellCheck, shell scripts में bugs खोजने वाला analysis tool है
  • वेबसाइट पर script paste करने पर editor के नीचे output window में check results तुरंत देखे जा सकते हैं
  • शामिल उदाहरण दिखाते हैं कि यह कई तरह की समस्याएं detect कर सकता है
    • आम scripts में छिपी कई समस्याएं
    • shebang sh होने पर आने वाली portability warnings
    • अधिक उच्च-स्तरीय semantic issues
    • अलग-अलग quote handling problems

installation, लाइसेंस, integration

  • लोकल installation cabal, apt, dnf, pkg, brew install से संभव है
  • यह GPLv3 लाइसेंस वाला free software है
  • दस्तावेज़ ShellCheck Wiki पर देखे जा सकते हैं
  • यह GitHub पर public है, और website code भी साथ में public है
  • distributions या package managers के लिए packages पहले से उपलब्ध हैं
  • प्रमुख editors में इसे integrated linter के रूप में इस्तेमाल किया जा सकता है
  • CodeClimate, Codacy, CodeFactor में GitHub repositories की automatic checking के लिए इसका उपयोग किया जा सकता है
  • ShellCheck Haskell में लिखा गया है

1 टिप्पणियां

 
GN⁺ 2023-11-25
Hacker News की रायें
  • मेरा इस्तेमाल किया जाने वाला tip यह है। shebang में लगभग हमेशा -u (nounset) डालना बेहतर होता है, ताकि घोषित न किए गए variables को error बनाया जा सके; जो exception अक्सर दिखता है वह "${arr[@]}" syntax से empty array expand करते समय unbound माना जाना है
    -n (noexec) command execution रोकता है, इसलिए इसे गरीब आदमी के dry-run की तरह इस्तेमाल किया जा सकता है। -e (errexit) भी उपयोगी है, लेकिन ध्यान रखना पड़ता है कि व्यवहार में सिर्फ fail हुई “वही” command exit trigger करती है, इसलिए मैं व्यक्तिगत तौर पर इसे avoid करता हूं और commands के बाद अक्सर || fail "..." जोड़ना पसंद करता हूं

    • "${arr[@]}" वाली समस्या सिर्फ bash 3 या उससे पुराने में है, और bash 4 से, variable सच में defined न हो तब भी [@] unbound variable नहीं फेंकता
      फिर भी macOS अब भी default रूप से bash v3 install करता है और auto-update भी नहीं करता, इसलिए यह समस्या बनी हुई है। bash 3 की आखिरी release 20 साल पहले हुई थी, यह सच में पागलपन है। empty array expansion में unbound आने को ${var+alter} expansion से bypass किया जा सकता है: echo "${arr+${arr[@]}}"
    • shebang में Bash/shell options न डालने की आम सलाह भी है। क्योंकि अगर कोई script को ./my_script.sh की बजाय bash my_script.sh की तरह interpreter स्पष्ट करके चलाता है, तो options apply नहीं होते
      executable bit set न करने के लिए काफी लोग ऐसा करते हैं, और कभी-कभी वजह समझ की कमी भी होती है। इसलिए shebang के बाद पहली line में set -euo pipefail जैसा set इस्तेमाल करने की सलाह आम है, और #!/usr/bin/env bash जैसे cases में भी मदद मिलती है जहां shebang के लिए extra arguments संभालना कठिन होता है
    • जिज्ञासा है कि -u को shebang में डालने की कोई खास वजह है क्या। set -u के बजाय shebang क्यों?
      Bash में "${arr[@]}" ठीक काम करता लगता है। जैसा दूसरे comments में भी बताया गया, नए Bash में यह और बेहतर हुआ है, और समस्या सिर्फ <= 4.3 में दिखती है: https://news.ycombinator.com/item?id=38397241
      उदाहरण के लिए bash -uc 'unset x; echo "=> ${x[@]}"' और bash -uc 'x=(); echo "=> ${x[@]}"' empty value के साथ pass हो जाते हैं, लेकिन bash -uc 'x=(); echo "=> ${x[0]}"' में bash: x[0]: unbound variable रह जाता है। Zsh पहले example को पसंद नहीं करता, लेकिन दोनों को bash -uc 'unset x; echo "=> ${x[@]:-null}"' की तरह default value expansion support करना चाहिए। -e functions के साथ जुड़ने पर बहुत confusing हो जाता है, इसलिए समय के साथ मुझे यह कम पसंद आने लगा है
    • -e की समस्या -o pipefail से अच्छी तरह संभाली जा सकती है, और यह पिछले साल से POSIX में शामिल है
    • script को जल्दी exit कराने वाले options इस्तेमाल करने पर, जब cleanup करने वाली files होती थीं, तो आम तौर पर साथ में trap भी इस्तेमाल करना पड़ता था
      trap एक शानदार scripting feature है, लेकिन लगता है इसके बारे में पर्याप्त बात नहीं होती
  • हाल में arithmetic expansion की वजह से shell script में privilege escalation vulnerability मिली। यह https://research.nccgroup.com/2020/05/12/shell-arithmetic-ex... में बताए गए type जैसी थी
    उदाहरण के लिए $((1 + ENV_VAR)) में अगर $ENV_VAR को control किया जा सके तो code injection संभव है। दुर्भाग्य से ShellCheck ने कम से कम default settings में इसे नहीं पकड़ा। लेकिन अगर आप security के लिहाज से जरा भी महत्वपूर्ण कुछ implement कर रहे हैं, तो शुरुआत से ही shell इस्तेमाल नहीं करना चाहिए

    • ज्यादा security चाहिए तो क्या इस्तेमाल करना चाहिए?
  • ShellCheck सच में उद्धारकर्ता है। पहले मैंने एक छोटा wrapper https://github.com/jamespwilliams/strictbash बनाया था, जिसे scripts के shebang के तौर पर इस्तेमाल किया जा सकता है
    script चलने से पहले ShellCheck चलाता है, और अगर failures हों तो script बिल्कुल execute नहीं होती, साथ ही bash के सभी “strict mode” flags भी set करता है। संदर्भ: http://redsymbol.net/articles/unofficial-bash-strict-mode/

    • अच्छा तो है, लेकिन लगता है यह सिर्फ अपनी scripts में इस्तेमाल करने पर कारगर होगा। वरना जिन भी scripts को चलाना है, उन्हें हर बार debug और fix करना पड़ेगा
  • यह विषय कई बार आ चुका है: https://news.ycombinator.com/from?site=shellcheck.net
    आखिरी बड़ी चर्चा 2021 में हुई थी, 301 points और 54 comments थे: https://news.ycombinator.com/item?id=27030504

  • कुछ समय पहले build और deployment scripts, और single production server के लिए कुछ bash scripts को Haskell के Turtle में बदल दिया था
    duplication काफी घटा सका, यह अच्छा लगा, और final code भी बहुत छोटा हो गया। https://hackage.haskell.org/package/turtle

    • हाल ही में Turtle इस्तेमाल करके देखा, लेकिन आखिर में उसे छोड़कर typed-process चुना
      मेरी जानकारी में Turtle program के पास सिर्फ एक current directory होती है, इसलिए ऐसे concurrent tasks चलाते समय मुश्किल होती है जिन्हें किसी खास directory में run होना चाहिए। locking, queue और worker approach से कुछ हद तक हल किया, लेकिन जब Turtle की current directory delete होने की वजह से failures शुरू हुए तो संभालना मुश्किल हो गया
      इसके उलट typed-process अलग process launch करता है, और cd किए बिना working directory के अंदर run कर सकता है, इसलिए बड़े और complex workflows के लिए अच्छा fit है। OverloadedStrings support भी अच्छा है, इसलिए आम तौर पर bash में जो type करते, उसे copy-paste करें तो वैसा ही काम करता है
      raw strings को source code में बेहतर दिखाने के लिए interpolate package और QuasiQuotes भी इस्तेमाल करता हूं, लेकिन यह hlint के compatible नहीं है, इसलिए strings handling के लिए कोई और package देखने का सोच रहा हूं
  • साफ-साफ self-promotion है, लेकिन commit से पहले या कम से कम merge से पहले सारी warnings ठीक करता हूं—इस principle के साथ ShellCheck और कई linters को pre-commit setup में डाल रखा है
    लेकिन मेरे ज्यादातर projects में shell .gitlab-ci.yml files के अंदर होता है, इसलिए check करना मुश्किल है। इसलिए इसे automatically handle करने वाला wrapper बनाया: https://pypi.org/project/glscpc/
    ShellCheck project और थोड़ी magic के साथ, लगभग सही line numbers के साथ ShellCheck की findings दिखाता है

    • अच्छा होगा अगर कोई project यह काम और generic तरीके से करे
      GitLab CI इस्तेमाल नहीं करता, लेकिन Dockerfile, GitHub Actions, Justfile जैसे file formats काफी इस्तेमाल करता हूं, जिनमें मूल रूप से shell script inline डाली जाती है
      आम तौर पर ShellCheck के लिए ही सही, कुछ commands से ज्यादा complex चीजों को अलग shell script में निकाल देता हूं और Dockerfile की inline script से उसे call कराता हूं। यह pattern CI को GitHub Actions से बहुत ज्यादा बंधने से बचाने में भी मदद करता है
    • हाई-फाइव। मैंने भी बहुत हाल में ऐसा ही कुछ बनाया: https://gitlab.com/engmark/shellcheck-gitlab-ci-scripts-hook
      usage example .gitlab-ci.yml के लिए pre-commit hook है, और configuration example यहां है: https://gitlab.com/engmark/root/-/blob/9f7d9b93c2297d0b170e5...
    • सच में बढ़िया। पहले मैंने इस समस्या को दूसरे angle से solve करने की कोशिश की थी। एक preprocessing step डालना चाहता था, जो “normal” shell script लेकर build time पर उस job के script हिस्से में render करे
      फायदा यह था कि सब कुछ अब भी gitlab-ci job के अंदर self-contained रहता। लेकिन GitLab CI runner environment में shell की तमाम अजीबोगरीब चीजों से निपटना बहुत दर्दनाक था, इसलिए बंद कर दिया, और अब सारे jobs को Python script में shift कर रहा हूं
  • bash language server भी है: https://github.com/bash-lsp/bash-language-server/

  • अच्छा है। पहली बार run किए गए production /bin/sh script से तुरंत कुछ चीजें सीखीं, जबकि ऐसे scripts 80s से संभालता आया हूं

  • अगर Bash में लिखने के लिए इतना लंबा हो रहा है कि असल में ऐसा करना ही नहीं चाहिए, तो https://github.com/bach-sh/bach भी recommend करूंगा

  • ShellCheck शानदार है, लेकिन source/import handling सच में painful है। यह ShellCheck की गलती नहीं है, sh ही nightmare है

    • इस तरह किया जा सकता है: # shellcheck source=./deployment/deployment-example.env के बाद . "${1}" इस्तेमाल करें
      हालांकि जब कई sub-shell scripts और source करने वाली files बढ़ जाती हैं, तो समझ आ जाता है कि यह क्यों painful है