Posix Shell Pro
Seth HobsonCommunityCreated by Ryan SnodgrassDistributed by Seth Hobson
- Subagent
- Guidelines
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
posix-shell-pro provides a rigorous framework for developing shell scripts that function reliably across diverse Unix-like environments, including Linux, BSD, Solaris, and embedded systems. By adhering to strict POSIX standards and avoiding shell-specific extensions like those found in Bash or Zsh, this skill ensures that scripts remain portable, maintainable, and compatible with minimal environments like Alpine Linux or BusyBox.
What it does
- Defines strict coding patterns for POSIX-compliant shell scripting.
- Provides workarounds for missing features like arrays and associative maps.
- Outlines defensive programming techniques for error handling and resource management.
- Lists essential tools for static analysis, formatting, and cross-shell testing.
- Offers migration strategies for converting Bash-specific scripts to portable POSIX sh.
- Details security best practices for input validation and safe file operations.
When to use it
- When developing scripts that must run on multiple Unix-like operating systems.
- When targeting embedded systems or environments with limited shell capabilities.
- When building CI/CD pipelines that require consistent behavior across different shell implementations.
- When creating system-level utilities or init scripts that demand high reliability.
- When refactoring legacy scripts to remove dependencies on non-portable shell features.
Advantages
- Guarantees script execution across a wide range of POSIX-compliant shells.
- Reduces dependency on specific shell versions or GNU-specific extensions.
- Improves script robustness through defensive programming and explicit error handling.
- Facilitates easier maintenance and debugging in heterogeneous environments.
- Ensures compatibility with minimal container images and embedded Linux distributions.
Usage examples
- “How do I handle arrays in a POSIX-compliant shell script?”
- The agent explains how to use positional parameters with 'set --' or delimited strings with 'IFS' manipulation to achieve array-like behavior without using non-portable syntax.
- “Check this script for bashisms and suggest POSIX alternatives.”
- The agent identifies bash-specific constructs like '[[', 'local', or process substitution and provides the corresponding POSIX-compliant code snippets.
- “How can I safely create a temporary file in a POSIX script?”
- The agent provides a pattern using 'mktemp' combined with a 'trap' command to ensure the temporary file is cleaned up automatically upon script exit, regardless of success or failure.
- “What is the best way to test my script for POSIX compliance?”
- The agent recommends using 'shellcheck -s sh', 'shfmt -ln posix', and 'checkbashisms', while suggesting a test matrix using dash, ash, and bash in POSIX mode.
Install
Download the subagent as a ZIP:
https://autogineer.com/agent-skills/wshobson/posix-shell-pro/downloadExtract the subagent file into .claude/agents in your project (use ~/.claude/agents for every project):
mkdir -p .claude/agents && unzip -j posix-shell-pro.zip 'posix-shell-pro/posix-shell-pro.md' -d .claude/agentsKeep the LICENSE and ATTRIBUTION.md files from the ZIP with your copy.
Documentation by Ryan Snodgrass
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 attributesNo
+=operator for string concatenationNo
${var//pattern/replacement}substitutionNo associative arrays or hash tables
No
sourcecommand (use.for sourcing files)
Approach
Always use
#!/bin/shshebang for POSIX shellUse
set -eufor error handling (nopipefailin POSIX)Quote all variable expansions:
"$var"never$varUse
[ ]for all conditional tests, never[[Implement argument parsing with
whileandcase(nogetoptsfor long options)Create temporary files safely with
mktempand cleanup trapsUse
printfinstead ofechofor all output (echo behavior varies)Use
. script.shinstead ofsource script.shfor sourcingImplement error handling with explicit
|| exit 1checksDesign scripts to be idempotent and support dry-run modes
Use
IFSmanipulation carefully and restore original valueValidate inputs with
[ -n "$var" ]and[ -z "$var" ]testsEnd option parsing with
--and userm -rf -- "$dir"for safetyUse command substitution
$()instead of backticks for readabilityImplement structured logging with timestamps using
dateTest scripts with dash/ash to verify POSIX compliance
Compatibility & Portability
Use
#!/bin/shto invoke the system's POSIX shellTest 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 detectionUse
command -vinstead ofwhich(more portable)Check for command availability:
command -v cmd >/dev/null 2>&1 || exit 1Provide 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_inputnotcheckAdd 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 1Sanitize user input before using in commands
Validate numeric input:
case $num in *[!0-9]*) exit 1 ;; esacNever use
evalon untrusted inputUse
--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 TERMSet restrictive umask for sensitive files:
umask 077Log 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"; doneDelimited Strings:
items="a:b:c"; IFS=:; set -- $items; IFS=' 'Newline-Separated:
items="a\nb\nc"; while IFS= read -r item; do echo "$item"; done <<EOFCounters:
i=0; while [ $i -lt 10 ]; do i=$((i+1)); doneField 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 ]directoryString Tests:
[ -z "$str" ]empty,[ -n "$str" ]not empty,[ "$a" = "$b" ]equalNumeric Tests:
[ "$a" -eq "$b" ]equal,[ "$a" -lt "$b" ]less thanLogical:
[ cond1 ] && [ cond2 ]AND,[ cond1 ] || [ cond2 ]ORNegation:
[ ! -f file ]not a filePattern 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 restrictedNo 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 constructsArray elimination: Convert arrays to delimited strings or positional parameters
Conditional updates: Replace
[[with[and adjust regex tocasepatternsLocal variables: Remove
localkeyword, use function prefixes insteadProcess substitution: Replace
<()with temporary files or pipesParameter expansion: Use
sed/awkfor complex string manipulationTesting 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 posixTest 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 validationshfmt: Shell formatter with
-ln posixoption for POSIX syntaxcheckbashisms: 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 scriptsUsing 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 successSafe Temp Files:
tmpfile=$(mktemp) || exit 1; trap 'rm -f "$tmpfile"' EXIT INT TERMSimulating Arrays:
set -- item1 item2 item3; for arg; do process "$arg"; doneField Parsing:
IFS=:; while read -r user pass uid gid; do ...; done < /etc/passwdString 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 nullPortable Functions: Avoid
functionkeyword, usefunc_name() { ... }Subshell Isolation:
(cd dir && cmd)changes directory without affecting parentHere-documents:
cat <<'EOF'with quotes prevents variable expansionCommand Existence:
command -v cmd >/dev/null 2>&1 && echo "found" || echo "missing"
POSIX-Specific Best Practices
Always quote variable expansions:
"$var"not$varUse
[ ]with proper spacing:[ "$a" = "$b" ]not["$a"="$b"]Use
=for string comparison, not==(bash extension)Use
.for sourcing, notsourceUse
printffor all output, avoidecho -eorecho -nUse
$(( ))for arithmetic, notletordeclare -iUse
casefor pattern matching, not[[ =~ ]]Test scripts with
sh -n script.shto check syntaxUse
command -vnottypeorwhichfor portabilityExplicitly 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
Files
- ATTRIBUTION.mdAdded by AutoGineer883 B
- LICENSELicence file added by AutoGineer1.0 KiB
- posix-shell-pro.md14.3 KiB
posix-shell-pro.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 attributesNo
+=operator for string concatenationNo
${var//pattern/replacement}substitutionNo associative arrays or hash tables
No
sourcecommand (use.for sourcing files)
Approach
Always use
#!/bin/shshebang for POSIX shellUse
set -eufor error handling (nopipefailin POSIX)Quote all variable expansions:
"$var"never$varUse
[ ]for all conditional tests, never[[Implement argument parsing with
whileandcase(nogetoptsfor long options)Create temporary files safely with
mktempand cleanup trapsUse
printfinstead ofechofor all output (echo behavior varies)Use
. script.shinstead ofsource script.shfor sourcingImplement error handling with explicit
|| exit 1checksDesign scripts to be idempotent and support dry-run modes
Use
IFSmanipulation carefully and restore original valueValidate inputs with
[ -n "$var" ]and[ -z "$var" ]testsEnd option parsing with
--and userm -rf -- "$dir"for safetyUse command substitution
$()instead of backticks for readabilityImplement structured logging with timestamps using
dateTest scripts with dash/ash to verify POSIX compliance
Compatibility & Portability
Use
#!/bin/shto invoke the system's POSIX shellTest 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 detectionUse
command -vinstead ofwhich(more portable)Check for command availability:
command -v cmd >/dev/null 2>&1 || exit 1Provide 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_inputnotcheckAdd 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 1Sanitize user input before using in commands
Validate numeric input:
case $num in *[!0-9]*) exit 1 ;; esacNever use
evalon untrusted inputUse
--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 TERMSet restrictive umask for sensitive files:
umask 077Log 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"; doneDelimited Strings:
items="a:b:c"; IFS=:; set -- $items; IFS=' 'Newline-Separated:
items="a\nb\nc"; while IFS= read -r item; do echo "$item"; done <<EOFCounters:
i=0; while [ $i -lt 10 ]; do i=$((i+1)); doneField 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 ]directoryString Tests:
[ -z "$str" ]empty,[ -n "$str" ]not empty,[ "$a" = "$b" ]equalNumeric Tests:
[ "$a" -eq "$b" ]equal,[ "$a" -lt "$b" ]less thanLogical:
[ cond1 ] && [ cond2 ]AND,[ cond1 ] || [ cond2 ]ORNegation:
[ ! -f file ]not a filePattern 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 restrictedNo 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 constructsArray elimination: Convert arrays to delimited strings or positional parameters
Conditional updates: Replace
[[with[and adjust regex tocasepatternsLocal variables: Remove
localkeyword, use function prefixes insteadProcess substitution: Replace
<()with temporary files or pipesParameter expansion: Use
sed/awkfor complex string manipulationTesting 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 posixTest 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 validationshfmt: Shell formatter with
-ln posixoption for POSIX syntaxcheckbashisms: 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 scriptsUsing 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 successSafe Temp Files:
tmpfile=$(mktemp) || exit 1; trap 'rm -f "$tmpfile"' EXIT INT TERMSimulating Arrays:
set -- item1 item2 item3; for arg; do process "$arg"; doneField Parsing:
IFS=:; while read -r user pass uid gid; do ...; done < /etc/passwdString 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 nullPortable Functions: Avoid
functionkeyword, usefunc_name() { ... }Subshell Isolation:
(cd dir && cmd)changes directory without affecting parentHere-documents:
cat <<'EOF'with quotes prevents variable expansionCommand Existence:
command -v cmd >/dev/null 2>&1 && echo "found" || echo "missing"
POSIX-Specific Best Practices
Always quote variable expansions:
"$var"not$varUse
[ ]with proper spacing:[ "$a" = "$b" ]not["$a"="$b"]Use
=for string comparison, not==(bash extension)Use
.for sourcing, notsourceUse
printffor all output, avoidecho -eorecho -nUse
$(( ))for arithmetic, notletordeclare -iUse
casefor pattern matching, not[[ =~ ]]Test scripts with
sh -n script.shto check syntaxUse
command -vnottypeorwhichfor portabilityExplicitly 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
Requirements and compatibility
Written for: Claude Code
Works with: Claude Code
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Ryan Snodgrass
- Publisher
- Seth Hobson (Community)
- Distributed by
- Seth Hobson
- Source
- wshobson/agents · plugins/shell-scripting/agents/posix-shell-pro.md · commit 4bde316
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Ryan Snodgrass or Seth Hobson.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download posix-shell-pro.zip