Use this skill when
- •Working on posix shell pro tasks or workflows
- •Needing guidance, best practices, or checklists for posix shell pro
Do not use this skill when
- •The task is unrelated to posix shell pro
- •You need a different domain or tool outside this scope
Instructions
- •Clarify goals, constraints, and required inputs.
- •Apply relevant best practices and validate outcomes.
- •Provide actionable steps and verification.
- •If detailed examples are required, open
resources/implementation-playbook.md.
Focus Areas
- •Strict POSIX compliance for maximum portability
- •Shell-agnostic scripting that works on any Unix-like system
- •Defensive programming with portable error handling
- •Safe argument parsing without bash-specific features
- •Portable file operations and resource management
- •Cross-platform compatibility (Linux, BSD, Solaris, AIX, macOS)
- •Testing with dash, ash, and POSIX mode validation
- •Static analysis with ShellCheck in POSIX mode
- •Minimalist approach using only POSIX-specified features
- •Compatibility with legacy systems and embedded environments
POSIX Constraints
- •No arrays (use positional parameters or delimited strings)
- •No
[[conditionals (use[test command only) - •No process substitution
<()or>() - •No brace expansion
{1..10} - •No
localkeyword (use function-scoped variables carefully) - •No
declare,typeset, orreadonlyfor variable attributes - •No
+=operator for string concatenation - •No
${var//pattern/replacement}substitution - •No associative arrays or hash tables
- •No
sourcecommand (use.for sourcing files)
Approach
- •Always use
#!/bin/shshebang for POSIX shell - •Use
set -eufor error handling (nopipefailin POSIX) - •Quote all variable expansions:
"$var"never$var - •Use
[ ]for all conditional tests, never[[ - •Implement argument parsing with
whileandcase(nogetoptsfor long options) - •Create temporary files safely with
mktempand cleanup traps - •Use
printfinstead ofechofor all output (echo behavior varies) - •Use
. script.shinstead ofsource script.shfor sourcing - •Implement error handling with explicit
|| exit 1checks - •Design scripts to be idempotent and support dry-run modes
- •Use
IFSmanipulation carefully and restore original value - •Validate inputs with
[ -n "$var" ]and[ -z "$var" ]tests - •End option parsing with
--and userm -rf -- "$dir"for safety - •Use command substitution
$()instead of backticks for readability - •Implement structured logging with timestamps using
date - •Test scripts with dash/ash to verify POSIX compliance
Compatibility & Portability
- •Use
#!/bin/shto invoke the system's POSIX shell - •Test on multiple shells: dash (Debian/Ubuntu default), ash (Alpine/BusyBox), bash --posix
- •Avoid GNU-specific options; use POSIX-specified flags only
- •Handle platform differences:
uname -sfor OS detection - •Use
command -vinstead ofwhich(more portable) - •Check for command availability:
command -v cmd >/dev/null 2>&1 || exit 1 - •Provide portable implementations for missing utilities
- •Use
[ -e "$file" ]for existence checks (works on all systems) - •Avoid
/dev/stdin,/dev/stdout(not universally available) - •Use explicit redirection instead of
&>(bash-specific)
Readability & Maintainability
- •Use descriptive variable names in UPPER_CASE for exports, lower_case for locals
- •Add section headers with comment blocks for organization
- •Keep functions under 50 lines; extract complex logic
- •Use consistent indentation (spaces only, typically 2 or 4)
- •Document function purpose and parameters in comments
- •Use meaningful names:
validate_inputnotcheck - •Add comments for non-obvious POSIX workarounds
- •Group related functions with descriptive headers
- •Extract repeated code into functions
- •Use blank lines to separate logical sections
Safety & Security Patterns
- •Quote all variable expansions to prevent word splitting
- •Validate file permissions before operations:
[ -r "$file" ] || exit 1 - •Sanitize user input before using in commands
- •Validate numeric input:
case $num in *[!0-9]*) exit 1 ;; esac - •Never use
evalon untrusted input - •Use
--to separate options from arguments:rm -- "$file" - •Validate required variables:
[ -n "$VAR" ] || { echo "VAR required" >&2; exit 1; } - •Check exit codes explicitly:
cmd || { echo "failed" >&2; exit 1; } - •Use
trapfor cleanup:trap 'rm -f "$tmpfile"' EXIT INT TERM - •Set restrictive umask for sensitive files:
umask 077 - •Log security-relevant operations to syslog or file
- •Validate file paths don't contain unexpected characters
- •Use full paths for commands in security-critical scripts:
/bin/rmnotrm
Performance Optimization
- •Use shell built-ins over external commands when possible
- •Avoid spawning subshells in loops: use
while readnotfor i in $(cat) - •Cache command results in variables instead of repeated execution
- •Use
casefor multiple string comparisons (faster than repeatedif) - •Process files line-by-line for large files
- •Use
expror$(( ))for arithmetic (POSIX supports$(( ))) - •Minimize external command calls in tight loops
- •Use
grep -qwhen you only need true/false (faster than capturing output) - •Batch similar operations together
- •Use here-documents for multi-line strings instead of multiple echo calls
Documentation Standards
- •Implement
-hflag for help (avoid--helpwithout proper parsing) - •Include usage message showing synopsis and options
- •Document required vs optional arguments clearly
- •List exit codes: 0=success, 1=error, specific codes for specific failures
- •Document prerequisites and required commands
- •Add header comment with script purpose and author
- •Include examples of common usage patterns
- •Document environment variables used by script
- •Provide troubleshooting guidance for common issues
- •Note POSIX compliance in documentation
Working Without Arrays
Since POSIX sh lacks arrays, use these patterns:
- •Positional Parameters:
set -- item1 item2 item3; for arg; do echo "$arg"; done - •Delimited Strings:
items="a:b:c"; IFS=:; set -- $items; IFS=' ' - •Newline-Separated:
items="a\nb\nc"; while IFS= read -r item; do echo "$item"; done <<EOF - •Counters:
i=0; while [ $i -lt 10 ]; do i=$((i+1)); done - •Field Splitting: Use
cut,awk, or parameter expansion for string splitting
Portable Conditionals
Use [ ] test command with POSIX operators:
- •File Tests:
[ -e file ]exists,[ -f file ]regular file,[ -d dir ]directory - •String Tests:
[ -z "$str" ]empty,[ -n "$str" ]not empty,[ "$a" = "$b" ]equal - •Numeric Tests:
[ "$a" -eq "$b" ]equal,[ "$a" -lt "$b" ]less than - •Logical:
[ cond1 ] && [ cond2 ]AND,[ cond1 ] || [ cond2 ]OR - •Negation:
[ ! -f file ]not a file - •Pattern Matching: Use
casenot[[ =~ ]]
CI/CD Integration
- •Matrix testing: Test across dash, ash, bash --posix, yash on Linux, macOS, Alpine
- •Container testing: Use alpine:latest (ash), debian:stable (dash) for reproducible tests
- •Pre-commit hooks: Configure checkbashisms, shellcheck -s sh, shfmt -ln posix
- •GitHub Actions: Use shellcheck-problem-matchers with POSIX mode
- •Cross-platform validation: Test on Linux, macOS, FreeBSD, NetBSD
- •BusyBox testing: Validate on BusyBox environments for embedded systems
- •Automated releases: Tag versions and generate portable distribution packages
- •Coverage tracking: Ensure test coverage across all POSIX shells
- •Example workflow:
shellcheck -s sh *.sh && shfmt -ln posix -d *.sh && checkbashisms *.sh
Embedded Systems & Limited Environments
- •BusyBox compatibility: Test with BusyBox's limited ash implementation
- •Alpine Linux: Default shell is BusyBox ash, not bash
- •Resource constraints: Minimize memory usage, avoid spawning excessive processes
- •Missing utilities: Provide fallbacks when common tools unavailable (
mktemp,seq) - •Read-only filesystems: Handle scenarios where
/tmpmay be restricted - •No coreutils: Some environments lack GNU coreutils extensions
- •Signal handling: Limited signal support in minimal environments
- •Startup scripts: Init scripts must be POSIX for maximum compatibility
- •Example: Check for mktemp:
command -v mktemp >/dev/null 2>&1 || mktemp() { ... }
Migration from Bash to POSIX sh
- •Assessment: Run
checkbashismsto identify bash-specific constructs - •Array elimination: Convert arrays to delimited strings or positional parameters
- •Conditional updates: Replace
[[with[and adjust regex tocasepatterns - •Local variables: Remove
localkeyword, use function prefixes instead - •Process substitution: Replace
<()with temporary files or pipes - •Parameter expansion: Use
sed/awkfor complex string manipulation - •Testing strategy: Incremental conversion with continuous validation
- •Documentation: Note any POSIX limitations or workarounds
- •Gradual migration: Convert one function at a time, test thoroughly
- •Fallback support: Maintain dual implementations during transition if needed
Quality Checklist
- •Scripts pass ShellCheck with
-s shflag (POSIX mode) - •Code is formatted consistently with shfmt using
-ln posix - •Test on multiple shells: dash, ash, bash --posix, yash
- •All variable expansions are properly quoted
- •No bash-specific features used (arrays,
[[,local, etc.) - •Error handling covers all failure modes
- •Temporary resources cleaned up with EXIT trap
- •Scripts provide clear usage information
- •Input validation prevents injection attacks
- •Scripts portable across Unix-like systems (Linux, BSD, Solaris, macOS, Alpine)
- •BusyBox compatibility validated for embedded use cases
- •No GNU-specific extensions or flags used
Output
- •POSIX-compliant shell scripts maximizing portability
- •Test suites using shellspec or bats-core validating across dash, ash, yash
- •CI/CD configurations for multi-shell matrix testing
- •Portable implementations of common patterns with fallbacks
- •Documentation on POSIX limitations and workarounds with examples
- •Migration guides for converting bash scripts to POSIX sh incrementally
- •Cross-platform compatibility matrices (Linux, BSD, macOS, Solaris, Alpine)
- •Performance benchmarks comparing different POSIX shells
- •Fallback implementations for missing utilities (mktemp, seq, timeout)
- •BusyBox-compatible scripts for embedded and container environments
- •Package distributions for various platforms without bash dependency
Essential Tools
Static Analysis & Formatting
- •ShellCheck: Static analyzer with
-s shfor POSIX mode validation - •shfmt: Shell formatter with
-ln posixoption for POSIX syntax - •checkbashisms: Detects bash-specific constructs in scripts (from devscripts)
- •Semgrep: SAST with POSIX-specific security rules
- •CodeQL: Security scanning for shell scripts
POSIX Shell Implementations for Testing
- •dash: Debian Almquist Shell - lightweight, strict POSIX compliance (primary test target)
- •ash: Almquist Shell - BusyBox default, embedded systems
- •yash: Yet Another Shell - strict POSIX conformance validation
- •posh: Policy-compliant Ordinary Shell - Debian policy compliance
- •osh: Oil Shell - modern POSIX-compatible shell with better error messages
- •bash --posix: GNU Bash in POSIX mode for compatibility testing
Testing Frameworks
- •bats-core: Bash testing framework (works with POSIX sh)
- •shellspec: BDD-style testing that supports POSIX sh
- •shunit2: xUnit-style framework with POSIX sh support
- •sharness: Test framework used by Git (POSIX-compatible)
Common Pitfalls to Avoid
- •Using
[[instead of[(bash-specific) - •Using arrays (not in POSIX sh)
- •Using
localkeyword (bash/ksh extension) - •Using
echowithoutprintf(behavior varies across implementations) - •Using
sourceinstead of.for sourcing scripts - •Using bash-specific parameter expansion:
${var//pattern/replacement} - •Using process substitution
<()or>() - •Using
functionkeyword (ksh/bash syntax) - •Using
$RANDOMvariable (not in POSIX) - •Using
read -afor arrays (bash-specific) - •Using
set -o pipefail(bash-specific) - •Using
&>for redirection (use>file 2>&1)
Advanced Techniques
- •Error Trapping:
trap 'echo "Error at line $LINENO" >&2; exit 1' EXIT; trap - EXITon success - •Safe Temp Files:
tmpfile=$(mktemp) || exit 1; trap 'rm -f "$tmpfile"' EXIT INT TERM - •Simulating Arrays:
set -- item1 item2 item3; for arg; do process "$arg"; done - •Field Parsing:
IFS=:; while read -r user pass uid gid; do ...; done < /etc/passwd - •String Replacement:
echo "$str" | sed 's/old/new/g'or use parameter expansion${str%suffix} - •Default Values:
value=${var:-default}assigns default if var unset or null - •Portable Functions: Avoid
functionkeyword, usefunc_name() { ... } - •Subshell Isolation:
(cd dir && cmd)changes directory without affecting parent - •Here-documents:
cat <<'EOF'with quotes prevents variable expansion - •Command Existence:
command -v cmd >/dev/null 2>&1 && echo "found" || echo "missing"
POSIX-Specific Best Practices
- •Always quote variable expansions:
"$var"not$var - •Use
[ ]with proper spacing:[ "$a" = "$b" ]not["$a"="$b"] - •Use
=for string comparison, not==(bash extension) - •Use
.for sourcing, notsource - •Use
printffor all output, avoidecho -eorecho -n - •Use
$(( ))for arithmetic, notletordeclare -i - •Use
casefor pattern matching, not[[ =~ ]] - •Test scripts with
sh -n script.shto check syntax - •Use
command -vnottypeorwhichfor portability - •Explicitly handle all error conditions with
|| exit 1
References & Further Reading
POSIX Standards & Specifications
- •POSIX Shell Command Language - Official POSIX.1-2024 specification
- •POSIX Utilities - Complete list of POSIX-mandated utilities
- •Autoconf Portable Shell Programming - Comprehensive portability guide from GNU
Portability & Best Practices
- •Rich's sh (POSIX shell) tricks - Advanced POSIX shell techniques
- •Suckless Shell Style Guide - Minimalist POSIX sh patterns
- •FreeBSD Porter's Handbook - Shell - BSD portability considerations
Tools & Testing
- •checkbashisms - Detect bash-specific constructs