Bash coding
Skill nguyenthdat/opencode-manager/registry/skills/bash-coding
Comprehensive idiomatic Bash/POSIX Shell guidance: 134 prioritized rules across 12 categories. Use when writing, reviewing, refactoring, or debugging shell scripts (`.sh`, `.bash`, `#!/bin/bash`, `#!/bin/sh`). Covers variable handling/quoting, error handling (errexit/pipefail), I/O and redirection, portability (POSIX sh vs Bash), security (injection/suid), function design, testing (Bats/shellspec), and anti-patterns. Target Bash 5.x+; distinguish Bash-isms from POSIX sh for portable scripts.From its SKILL.md
npx -y skills add nguyenthdat/opencode-manager --skill bash-codingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
20.4 KB, ~5.6k tokens by cl100k_base, as published. Nobody here has run it
Bash / Shell Best Practices
Comprehensive guide for writing high-quality, idiomatic, and robust Bash/POSIX shell scripts. Contains 134 rules across 12 categories, prioritized by impact to guide LLMs in code generation and refactoring.
When to Apply
Reference these guidelines when:
- Writing new Bash scripts or shell functions
- Reviewing shell scripts for correctness and security
- Debugging variable scoping, quoting, or exit code issues
- Refactoring legacy shell scripts to modern standards
- Writing scripts that must run on both Linux and macOS
- Choosing between Bash features and POSIX-compatible alternatives
- Setting up CI/CD with ShellCheck and Bats testing
- Handling user input, secrets, or temporary files securely
Bash 5.x & Modern Shell Features
This skill targets Bash 5.x (released 2019) while noting POSIX compatibility. Key features to leverage:
- Associative arrays (
declare -A): string-keyed maps for configuration, caching, dispatch tables (Bash 4.0+) - Namerefs (
declare -n,local -n): safe indirect variable/array access, replacingeval(Bash 4.3+) - Process substitution (
<(cmd),>(cmd)): pipe without subshells, preserve variable state (Bash 4.0+) - Here-strings (
<<<): single-string stdin without pipes or forks (Bash 4.0+) [[ ]]conditional: regex matching (=~), pattern matching (==), safe unquoted variablesglobstar(shopt -s globstar): recursive**/*.txtglob patterns (Bash 4.0+)nullglob/failglob: control glob behavior for empty matchesextglob: extended patterns (@(),!(),+(),*(),?())readarray/mapfile: read file/command output directly into arraysBASH_REMATCH: capture regex groups from[[ =~ ]]matchingEPOCHSECONDS/EPOCHREALTIME: wall-clock timestamps withoutdatefork (Bash 5.0+)BASH_SOURCEvs$0: reliably determine script location regardless of how it's invoked- Subshell vs command grouping:
(cmd)is a subshell (variable changes lost),{ cmd; }is in-process
For POSIX sh (#!/bin/sh, dash, ash, busybox sh), avoid all of the above and see port-avoid-bashisms.
Rule Categories by Priority
| Priority | Category | Impact | Prefix | Rules |
|---|---|---|---|---|
| 1 | Variable Handling & Quoting | CRITICAL | var- | 14 |
| 2 | Error Handling & Exit Codes | CRITICAL | err- | 13 |
| 3 | Input/Output & Redirection | HIGH | io- | 12 |
| 4 | Security | HIGH | sec- | 11 |
| 5 | Function Design | HIGH | fn- | 11 |
| 6 | Portability (POSIX vs Bash) | HIGH | port- | 11 |
| 7 | Naming & Style Conventions | MEDIUM | name- | 11 |
| 8 | Arrays & Data Structures | MEDIUM | arr- | 9 |
| 9 | Testing (Bats/shellspec) | MEDIUM | test- | 10 |
| 10 | Debugging & Logging | MEDIUM | debug- | 9 |
| 11 | Performance & Efficiency | MEDIUM | perf- | 9 |
| 12 | Anti-patterns | REFERENCE | anti- | 14 |
Quick Reference
1. Variable Handling & Quoting (CRITICAL)
var-always-quote- Always quote variable expansions:"$var"not$varvar-brace-variables- Use${var}for disambiguation and clarityvar-default-values- Use${var:-default}and${var:=default}for defaultsvar-local-in-functions- Declare function variables withlocalvar-readonly-constants- Usereadonlyordeclare -rfor constantsvar-uppercase-env- UPPER_CASE for environment variables and globalsvar-null-vs-unset- Use${var+isset}vs${var-unset}for existence checksvar-indirect-reference- Usedeclare -n(nameref) instead ofevalvar-no-eval-expand- Never useevalfor variable expansionvar-arrays-cautious- Use arrays for lists; don't misuse strings with spacesvar-no-glob-wordsplitting- Set IFS carefully; disable word splitting for safetyvar-prefix-suffix-remove- Use${var#prefix},${var%suffix},${var//pattern/replacement}var-length-count- Use${#var}for string length,${#arr[@]}for array sizevar-avoid-export-in-loop- Don't export variables inside loops unnecessarily
2. Error Handling & Exit Codes (CRITICAL)
err-errexit-set- Useset -euo pipefailat top of scriptserr-pipefail-required- Always useset -o pipefailto catch pipe errorserr-check-exit-status- Check$?after critical commandserr-trap-errors- Usetrap '...' ERRfor error handlingerr-trap-exit-cleanup- Usetrap '...' EXITfor cleanuperr-meaningful-exit- Exit with meaningful non-zero codeserr-avoid-ignore-errors- Don't use|| trueto ignore errors without commenterr-command-exists- Checkcommand -vbefore using external toolserr-set-e-cautious- Understandset -eedge cases in conditionalserr-no-unchecked-cd- Always checkcdsuccess:cd dir || exit 1err-mkdir-parent- Usemkdir -pto avoid "already exists" errorserr-return-over-exit-fn- Usereturnin functions,exitonly at top levelerr-dry-run-pattern- Support--dry-runin destructive scripts
3. Input/Output & Redirection (HIGH)
io-heredoc-quote- Quote heredoc delimiter to prevent expansion:<<'EOF'io-stderr-redirect- Redirect stderr explicitly:2>/dev/nullor2>&1io-read-while-pipe- Usewhile IFS= read -r linepattern correctlyio-process-substitution- Use<(cmd)and>(cmd)for piping without subshellsio-avoid-cat-pipe- Avoidcat file | cmd; usecmd < fileorcmd fileio-file-descriptor-management- Useexecfor custom file descriptorsio-here-string- Use<<<for simple string inputio-tempfile-safely- Usemktempfor temporary files; never hardcode/tmpio-null-dev-null- Use/dev/nullexplicitly; avoid writing to stdoutio-read-r-preserve- Useread -rto preserve backslashesio-no-binary-in-pipe- Don't pipe binary data through text processingio-buffered-flush- Usestdbuforunbufferwhen needed for line-buffered pipes
4. Security (HIGH)
sec-no-unquoted-expansion- Quote all shell expansions to prevent word splitting/globsec-no-eval-user- Never useevalwith user-supplied inputsec-sanitize-input- Validate and sanitize user input before usesec-path-injection- Never trust$PATH; use absolute paths or set PATH explicitlysec-no-exec-user-test- Don't use user input in command namessec-tempfile-race- Usemktempto avoid TOCTOU racessec-suid-cautious- Avoid setuid shell scripts; usesudoinsteadsec-secrets-in-env- Pass secrets via environment, never CLI argssec-no-hardcoded-secrets- Use environment variables or secret managers for credentialssec-umask-restrictive- Setumask 077for scripts handling sensitive datasec-shellcheck-required- Runshellcheckon all shell scripts
5. Function Design (HIGH)
fn-return-values- Capture function output with$(...); usereturnfor statusfn-argument-count- Check$#for required argument countfn-argument-names- Name function arguments at top:local name=$1fn-main-function- Put main logic inmain()function; call at endfn-pure-when-possible- Write functions that don't depend on global statefn-small-focused- Keep functions small and single-purposefn-no-side-effects- Minimize side effects; document globals modifiedfn-usage-help- Provideusage()function with help textfn-option-parsing- Usegetoptsfor argument parsing; avoid manual$1$2loopsfn-library-source- Source library scripts; don't copy-paste functionsfn-lowercase-names- Use lowercase function names (POSIX namespace safety)
6. Portability (POSIX vs Bash) (HIGH)
port-shebang-choice- Use#!/usr/bin/env bashfor Bash;#!/bin/shfor POSIXport-avoid-bashisms- Avoid[[ ]], arrays,${!ref},source,==in POSIX sh scriptsport-posix-test- Use[ ]instead of[[ ]]when portability mattersport-printf-over-echo- Useprintfinstead ofechofor portable outputport-no-local-posix- Don't uselocalin POSIX sh (Bash-ism)port-command-v-which- Usecommand -voverwhichfor command checkingport-readlink-realpath- Don't assumereadlink -f/realpathis availableport-sed-i-portable- Usesed -i.bakfor portable in-place editingport-array-alternatives- Use IFS-delimited strings when arrays aren't availableport-shellcheck-directive- Use ShellCheck directives for bash-only scriptsport-coproc-portable- Document Bash-specific features when used
7. Naming & Style Conventions (MEDIUM)
name-variables-uppercase-env- UPPER_CASE for environment/export variablesname-variables-lowercase-local- lowercase_with_underscores for local varsname-functions-lowercase- lowercase function names; avoid UpperCasename-constants-readonly-readonly VARIABLE_NAMEfor constantsname-files-kebab-case- Use kebab-case.sh for script file namesname-library-prefix- Prefix library functions with namespace_ (e.g.,log_info)name-boolean-true-false- Use 0/1 for boolean return valuesname-descriptive-vars- Use descriptive variable names; avoid a, b, c, xname-no-reserved-words- Avoid bash keywords as function/variable namesname-globals-caps- UPPER_CASE for globals in scriptsname-temp-vars-prefix- Prefix temp variables with_to mark as internal
8. Arrays & Data Structures (MEDIUM)
arr-declare-arrays- Usedeclare -afor indexed,declare -Afor associativearr-expand-properly- Use"${arr[@]}"to expand arrays with proper quotingarr-iterate-keys- Iterate associative array keys with"${!arr[@]}"arr-append-elements- Usearr+=("new")to append to arraysarr-length-count- Use${#arr[@]}for array lengtharr-no-spaces-in-keys- Use sensible keys for associative arraysarr-pass-to-function- Pass arrays by name withdeclare -n(nameref)arr-slice-subset- Use${arr[@]:offset:length}for array slicingarr-read-into-array- Usereadarray/mapfilefor reading lines into array
9. Testing (Bats/shellspec) (MEDIUM)
test-bats-framework- Use Bats (Bash Automated Testing System) for testingtest-run-helper- Use Batsrunhelper to capture output and statustest-assert-output- Use[ "$output" = "expected" ]and[ "$status" -eq 0 ]test-setup-teardown- Usesetup()andteardown()for test fixturestest-mock-commands- Override functions or use stub scripts for mockingtest-temp-dirs- Create and clean test directories in setup/teardowntest-one-assertion- Test one behavior per test casetest-skip-not-installed- Skip tests when dependencies not installedtest-shellcheck-build- Run ShellCheck as part of the test suitetest-ci-integration- Output TAP format for CI integration
10. Debugging & Logging (MEDIUM)
debug-set-x-trace- Useset -xfor command tracing; wrap in subshelldebug-ps4-enhanced- Set enhanced PS4 for file:line:function tracesdebug-trap-debug- Usetrap '...' DEBUGfor custom debuggingdebug-log-function- Write alog()function with timestamps and levelsdebug-verbose-flag- Support--verbose/-vflag for debug outputdebug-no-echo-debug- Use stderr for debug, stdout for program outputdebug-assert-function- Createassert()helper for runtime checksdebug-color-output- Usetputor ANSI codes; check if output is terminaldebug-stack-trace- Print stack trace in ERR trap withcaller
11. Performance & Efficiency (MEDIUM)
perf-avoid-fork- Use builtins ([[ ]],${var##},printf) to avoid subshell forksperf-avoid-cat-useless- Avoid useless use of cat (UUOC)perf-inline-grep- Use Bash pattern matching overgrepwhen possibleperf-batch-process- Batch operations:gitcommands, database queriesperf-parallel-xargs- Usexargs -Pfor parallel executionperf-avoid-subshell-loop- Don't pipe intowhile; use process substitutionperf-here-string-speed- Here-strings (<<<) are faster thanecho |pipeperf-glob-over-find- Use glob patterns overfindfor simple directory walksperf-cache-results- Cache expensive command results in variables
12. Anti-patterns (REFERENCE)
anti-unquoted-variables- Never leave variable expansions unquotedanti-eval-everything- Don't useeval; find builtin alternativesanti-bare-variable-in-test- Don't use[ $var = "value" ]on empty varanti-pipe-while-subshell- Don't modify variables in pipewhileloops (subshell issue)anti-ls-in-for- Never usefor f in $(ls); use globs:for f in *anti-backticks- Use$()over backticks for command substitutionanti-newlines-in-names- Don't create files/vars with spaces or newlinesanti-cd-without-check- Always checkcdsuccessanti-echo-in-function-output- Don't echo debug info in functions that return dataanti-global-variable-everywhere- Don't use global variables for everythinganti-source-without-path- Don'tsourcescripts without specifying pathanti-single-brackets-bash- Don't use[ ]for compound tests in Bash (use[[ ]])anti-rm-rf-asterisk-danger- Never userm -rf $VAR/*without validating$VARanti-interactive-suppress- Don't useyes |in scripts; handle interaction properly
Recommended Shell Settings
#!/usr/bin/env bash
# Safer bash defaults — include at top of every script
set -euo pipefail
IFS=$'\n\t'
# Additional safety options (opt-in)
shopt -s nullglob # Non-matching globs → empty (not literal *)
shopt -s inherit_errexit # Subshells inherit errexit (Bash 4.4+)
shopt -s shift_verbose # shift errors when count exceeds $#
# For debuggable scripts:
# shopt -s failglob # Non-matching globs → error (stricter than nullglob)
How to Use
This skill provides rule identifiers for quick reference. When generating or reviewing shell scripts:
- Check relevant category based on task type
- Apply rules with matching prefix
- Prioritize CRITICAL > HIGH > MEDIUM > REFERENCE
- Read rule files in
rules/for detailed examples with Bad/Good code
Rule Application by Task
| Task | Primary Categories |
|---|---|
| New script skeleton | err-, fn-, name- |
| Variable handling | var-, sec- |
| Function writing | fn-, var- |
| File/pipe processing | io-, perf- |
| Security review | sec-, anti- |
| Cross-platform script | port-, name- |
| Test writing | test-, debug- |
| Debugging failures | debug-, err- |
| Code review | anti-, sec- |
Related Skills
- design-patterns — choosing and implementing GoF/idiomatic patterns in shell scripts (pipeline/filter, command dispatch).
- security-review — cross-language security/correctness review methodology (phases, finding format, severity guidance) applies to shell-script reviews; it does not yet ship a dedicated shell bug-class reference file, so apply the general workflow to injection, unsafe
eval/word-splitting, and TOCTOU risks.
Sources
This skill synthesizes best practices from:
- Google Shell Style Guide
- ShellCheck Wiki
- Bash Hackers Wiki
- Wooledge BashGuide
- POSIX Shell Command Language
- Production codebases: Git, Docker, Homebrew, various CI/CD pipelines
- Community conventions and ShellCheck diagnostics
What ships with it: 134 files
240.8 KB alongside SKILL.md
rules/
- anti-backticks.md1.1 KB
- anti-bare-variable-in-test.md1.4 KB
- anti-cd-without-check.md1.1 KB
- anti-echo-in-function-output.md1.6 KB
- anti-eval-everything.md1.2 KB
- anti-global-variable-everywhere.md1.7 KB
- anti-interactive-suppress.md1.6 KB
- anti-ls-in-for.md1.2 KB
- anti-newlines-in-names.md1.6 KB
- anti-pipe-while-subshell.md1.2 KB
- anti-rm-rf-asterisk-danger.md1.8 KB
- anti-single-brackets-bash.md1.8 KB
- anti-source-without-path.md1.4 KB
- anti-unquoted-variables.md1012 B
- arr-append-elements.md1.3 KB
- arr-declare-arrays.md1.9 KB
- arr-expand-properly.md1.8 KB
- arr-iterate-keys.md1.4 KB
- arr-length-count.md1.5 KB
- arr-no-spaces-in-keys.md1.3 KB
- arr-pass-to-function.md2.2 KB
- arr-read-into-array.md1.8 KB
- arr-slice-subset.md1.8 KB
- debug-assert-function.md2.2 KB
- debug-color-output.md2.0 KB
- debug-log-function.md2.2 KB
- debug-no-echo-debug.md1.7 KB
- debug-ps4-enhanced.md1.7 KB
- debug-set-x-trace.md1.7 KB
- debug-stack-trace.md2.3 KB
- debug-trap-debug.md1.7 KB
- debug-verbose-flag.md1.5 KB
- err-avoid-ignore-errors.md2.1 KB
- err-check-exit-status.md1.9 KB
- err-command-exists.md2.1 KB
- err-dry-run-pattern.md1.9 KB
- err-errexit-set.md1.8 KB
- err-meaningful-exit.md1.9 KB
- err-mkdir-parent.md1.7 KB
- err-no-unchecked-cd.md1.6 KB
94 more files not listed here. See all 134 in the repository.