Antigravity-awesome-skills posix-shell-pro
Expert in strict POSIX sh scripting for maximum portability across
install
source · Clone the upstream repo
git clone https://github.com/benjaminasterA/antigravity-awesome-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/benjaminasterA/antigravity-awesome-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/posix-shell-pro" ~/.claude/skills/benjaminastera-antigravity-awesome-skills-posix-shell-pro && rm -rf "$T"
manifest:
skills/posix-shell-pro/SKILL.mdsource content
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
keyword (use function-scoped variables carefully)local - No
,declare
, ortypeset
for variable attributesreadonly - No
operator for string concatenation+= - No
substitution${var//pattern/replacement} - No associative arrays or hash tables
- No
command (usesource
for sourcing files).
Approach
- Always use
shebang for POSIX shell#!/bin/sh - Use
for error handling (noset -eu
in POSIX)pipefail - Quote all variable expansions:
never"$var"$var - Use
for all conditional tests, never[ ][[ - Implement argument parsing with
andwhile
(nocase
for long options)getopts - Create temporary files safely with
and cleanup trapsmktemp - Use
instead ofprintf
for all output (echo behavior varies)echo - Use
instead of. script.sh
for sourcingsource script.sh - Implement error handling with explicit
checks|| exit 1 - Design scripts to be idempotent and support dry-run modes
- Use
manipulation carefully and restore original valueIFS - Validate inputs with
and[ -n "$var" ]
tests[ -z "$var" ] - End option parsing with
and use--
for safetyrm -rf -- "$dir" - 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
to invoke the system's POSIX shell#!/bin/sh - 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:
for OS detectionuname -s - Use
instead ofcommand -v
(more portable)which - Check for command availability:
command -v cmd >/dev/null 2>&1 || exit 1 - Provide portable implementations for missing utilities
- Use
for existence checks (works on all systems)[ -e "$file" ] - Avoid
,/dev/stdin
(not universally available)/dev/stdout - 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:
notvalidate_inputcheck - 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
on untrusted inputeval - 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
for cleanup:traptrap '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:
not/bin/rmrm
Performance Optimization
- Use shell built-ins over external commands when possible
- Avoid spawning subshells in loops: use
notwhile readfor i in $(cat) - Cache command results in variables instead of repeated execution
- Use
for multiple string comparisons (faster than repeatedcase
)if - Process files line-by-line for large files
- Use
orexpr
for arithmetic (POSIX supports$(( ))
)$(( )) - Minimize external command calls in tight loops
- Use
when you only need true/false (faster than capturing output)grep -q - Batch similar operations together
- Use here-documents for multi-line strings instead of multiple echo calls
Documentation Standards
- Implement
flag for help (avoid-h
without proper parsing)--help - 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
, or parameter expansion for string splittingawk
Portable Conditionals
Use
[ ] test command with POSIX operators:
- File Tests:
exists,[ -e file ]
regular file,[ -f file ]
directory[ -d dir ] - String Tests:
empty,[ -z "$str" ]
not empty,[ -n "$str" ]
equal[ "$a" = "$b" ] - Numeric Tests:
equal,[ "$a" -eq "$b" ]
less than[ "$a" -lt "$b" ] - Logical:
AND,[ cond1 ] && [ cond2 ]
OR[ cond1 ] || [ cond2 ] - Negation:
not a file[ ! -f file ] - Pattern Matching: Use
notcase[[ =~ ]]
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
may be restricted/tmp - 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
to identify bash-specific constructscheckbashisms - Array elimination: Convert arrays to delimited strings or positional parameters
- Conditional updates: Replace
with[[
and adjust regex to[
patternscase - Local variables: Remove
keyword, use function prefixes insteadlocal - Process substitution: Replace
with temporary files or pipes<() - Parameter expansion: Use
/sed
for complex string manipulationawk - 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
flag (POSIX mode)-s sh - 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,
,[[
, etc.)local - 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
for POSIX mode validation-s sh - shfmt: Shell formatter with
option for POSIX syntax-ln posix - 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
keyword (bash/ksh extension)local - Using
withoutecho
(behavior varies across implementations)printf - Using
instead ofsource
for sourcing scripts. - Using bash-specific parameter expansion:
${var//pattern/replacement} - Using process substitution
or<()>() - Using
keyword (ksh/bash syntax)function - Using
variable (not in POSIX)$RANDOM - Using
for arrays (bash-specific)read -a - Using
(bash-specific)set -o pipefail - Using
for redirection (use&>
)>file 2>&1
Advanced Techniques
- Error Trapping:
on successtrap 'echo "Error at line $LINENO" >&2; exit 1' EXIT; trap - EXIT - 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:
or use parameter expansionecho "$str" | sed 's/old/new/g'${str%suffix} - Default Values:
assigns default if var unset or nullvalue=${var:-default} - Portable Functions: Avoid
keyword, usefunctionfunc_name() { ... } - Subshell Isolation:
changes directory without affecting parent(cd dir && cmd) - Here-documents:
with quotes prevents variable expansioncat <<'EOF' - Command Existence:
command -v cmd >/dev/null 2>&1 && echo "found" || echo "missing"
POSIX-Specific Best Practices
- Always quote variable expansions:
not"$var"$var - Use
with proper spacing:[ ]
not[ "$a" = "$b" ]["$a"="$b"] - Use
for string comparison, not=
(bash extension)== - Use
for sourcing, not.source - Use
for all output, avoidprintf
orecho -eecho -n - Use
for arithmetic, not$(( ))
orletdeclare -i - Use
for pattern matching, notcase[[ =~ ]] - Test scripts with
to check syntaxsh -n script.sh - Use
notcommand -v
ortype
for portabilitywhich - 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