Writing POSIX-Compatible Shell Scripts

Write portable shell scripts that work across bash, dash, ash, and other POSIX shells. Avoid bashisms and ensure cross-platform compatibility.

POSIX Compatibility

Detailed Explanation

POSIX Shell Compatibility

If your script uses #!/bin/sh, it must conform to the POSIX shell specification. Many systems (Debian, Ubuntu) use dash as /bin/sh, which does not support Bash-specific features.

Common Bashisms to Avoid

Bashism POSIX Alternative
[[ condition ]] [ condition ]
== in [ ] =
${var//old/new} Use sed: `echo "$var"
Arrays arr=(a b) Use positional params or multiple vars
function name name()
source file . file
(( arithmetic )) $((arithmetic)) or expr
<<< (here-string) `echo "string"
select Manual menu loop
read -p printf "prompt: "; read var
Process sub <(cmd) Temp files or pipes

Testing POSIX Compliance

Method 1: Test with dash

dash ./script.sh

Method 2: Use checkbashisms

checkbashisms ./script.sh

Method 3: Use shellcheck with --shell=sh

shellcheck --shell=sh ./script.sh

POSIX-Safe Patterns

String operations:

# Get filename extension (POSIX)
ext="${filename##*.}"

# Get directory name (POSIX)
dir="${filepath%/*}"

# Remove prefix (POSIX)
base="${var#prefix}"

Arithmetic:

# POSIX arithmetic
count=$((count + 1))
result=$((a * b + c))

# POSIX comparison
if [ "$count" -gt 10 ]; then
  echo "More than 10"
fi

Local variables:

# local is not POSIX but widely supported
# For strict POSIX, use subshells for scoping:
my_func() (
  var="local to function"
  echo "$var"
)
# Parentheses instead of braces create a subshell

When to Use #!/bin/bash

Use bash when you need arrays, [[ ]], regex matching, process substitution, or other Bash-specific features. Just be explicit with #!/bin/bash or #!/usr/bin/env bash.

Use Case

System initialization scripts, package installation scripts, CI/CD pipelines that run on minimal Docker images (Alpine/busybox), and any script that must work across different Unix-like operating systems.

Try It — Shell Script Linter

Open full tool