POSIX Shell Script Generator

Generates a portable /bin/sh script skeleton, shebang, set -eu, an EXIT/INT/TERM cleanup trap, a logging function, and a getopts-based argument-parsing loop built from your flags, deliberately avoiding Bash-only features (arrays, [[ ]], local) so the script runs unmodified on dash, BusyBox ash (Alpine), and other minimal POSIX shells. A free online tool from Staaarter, right in your browser.

Runs locallyUpdated 2026-07-26

Overview

Introduction

A script written and tested against Bash often breaks the moment it runs inside an Alpine container, where /bin/sh is BusyBox's ash, not Bash, and Bash-only syntax like arrays or [[ ]] either errors out or silently behaves differently.

This tool generates a script skeleton that deliberately avoids every common bashism, so the result runs identically on dash, BusyBox ash, and Bash's own sh-compatibility mode.

What Is POSIX Shell Script Generator?

A POSIX /bin/sh script skeleton generator: shebang, set -eu, a trap on EXIT/INT/TERM (not the Bash-only ERR pseudo-signal) wired to a cleanup routine, a timestamped log() function, and a getopts argument-parsing loop, all built without arrays, [[ ]], or the local keyword.

Like the Bash Script Generator, you define custom single-letter flags with a description and variable name, but every generated construct here is checked against strict POSIX shell syntax rather than Bash's extended syntax.

How POSIX Shell Script Generator Works

Instead of Bash's `set -euo pipefail`, the script uses `set -eu`, since `pipefail` isn't part of POSIX; instead of a `local exit_code=$?` inside cleanup(), the script assigns to a plain (function-scoped-by-convention, not shell-enforced) global variable, since `local` is a widely supported but non-POSIX extension.

The trap only listens for EXIT, INT, and TERM, real signals plus the shell's own exit event, since POSIX trap has no equivalent to Bash's ERR pseudo-signal; correctness instead relies on `set -e` to stop the script the moment any command fails.

When To Use POSIX Shell Script Generator

Use it for any script that needs to run inside a minimal container image (Alpine, distroless, BusyBox-based) where Bash may not be installed at all.

It's also useful for Docker ENTRYPOINT or init scripts, which conventionally target /bin/sh precisely because it's guaranteed present even on the smallest base images.

Features

Advantages

  • Produces a script that runs unmodified across dash, BusyBox ash, and Bash's sh mode, avoiding a whole class of 'works on my machine, breaks in the container' bugs.
  • Explicitly avoids the specific bashisms (arrays, [[ ]], local, ERR trap) most likely to silently misbehave rather than error out clearly under a different shell.
  • Still gets you a real getopts argument-parsing loop, logging, and cleanup trapping, POSIX doesn't mean primitive.

Limitations

  • No array support means anything genuinely needing an ordered, indexable collection needs a different data structure (a newline-delimited string, or multiple positional variables) instead.
  • No ERR trap means the script can't automatically log the exact failing line the way the Bash generator's skeleton does; you rely on set -e stopping execution and your own log() calls around risky commands.
  • getopts alone (POSIX) only supports single-character flags, exactly like the Bash version, no --long-option parsing without a hand-rolled loop.

Examples

A Docker entrypoint script with a required mode flag

Input

scriptName: entrypoint.sh, purpose: Starts the app in the requested mode, flags: [{flag: m, hasArg: true, varName: MODE, description: Run mode (worker or web)}]

Output

#!/bin/sh
#
# entrypoint.sh - Starts the app in the requested mode
# POSIX sh: no arrays, no [[ ]], no 'local' - portable to dash/ash/BusyBox.
#
set -eu

SCRIPT_NAME="$(basename "$0")"

log() {
  printf '[%s] %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*" >&2
}

cleanup() {
  exit_code=$?
  # Add cleanup logic here (e.g. remove temp files, release locks)
  exit "$exit_code"
}

trap cleanup EXIT INT TERM

usage() {
  cat <<EOF
Usage: entrypoint.sh [-h] [-m value]

  -h          Show this help message
  -m value    Run mode (worker or web)
EOF
}

MODE=""

while getopts "hm:" opt; do
  case "$opt" in
    h) usage; exit 0 ;;
    m) MODE="$OPTARG" ;;
    \?) usage; exit 1 ;;
  esac
done
shift $((OPTIND - 1))

log "Starting $SCRIPT_NAME"

# Main script logic goes here
# Use case statements instead of [[ ]] for conditionals, and avoid bash arrays.

log "Done"

The skeleton runs identically whether the container's /bin/sh is dash, BusyBox ash, or Bash.

Best Practices & Notes

Best Practices

  • Test the generated script with dash explicitly (many distros ship it as /bin/sh already) even if you develop on a machine where /bin/sh happens to be Bash.
  • Use case statements for all conditionals in the logic you add, rather than reaching for [[ ]], which will fail outright under dash/ash.
  • Avoid relying on echo's flag behavior (like -e) since it differs across shells; prefer printf, exactly as the generated log() function does.

Developer Notes

The generator produces syntax that is valid under POSIX.1-2018's shell command language grammar specifically: no array assignment syntax, no local keyword, no [[ ]] compound command, and a trap that only names EXIT, INT, and TERM (real signals recognized by trap universally, unlike Bash's ERR pseudo-signal which POSIX doesn't define). The getopts optstring and case-based dispatch are otherwise structurally identical to the Bash Script Generator's output, since getopts itself is already a POSIX-specified builtin.

POSIX Shell Script Generator Use Cases

  • Writing a Docker ENTRYPOINT or init script for an Alpine or other minimal base image
  • Building a shell script intended to run across a variety of Linux distributions with different default /bin/sh implementations
  • Learning or auditing which specific constructs count as bashisms versus portable POSIX shell

Common Mistakes

  • Copying Bash idioms like arrays or [[ ]] into a script that's supposed to run under /bin/sh, which then fails specifically inside minimal containers where /bin/sh isn't Bash.
  • Assuming set -eu covers pipeline failures the way set -euo pipefail does in Bash; POSIX has no pipefail, so a failing early stage in a pipe can still be masked by a later stage's success.
  • Using local inside a function for a variable that should genuinely be scoped, since POSIX sh has no local, an accidental global reused elsewhere in the script needs an explicit unique name instead.

Tips

  • If you need Bash-only conveniences (arrays, [[ ]], associative arrays) and the target definitely has Bash installed, use the Bash Script Generator instead; this tool is specifically for when you can't assume that.
  • Run `shellcheck --shell=sh <script>` (or `checkbashisms` in Debian's devscripts package) after filling in your logic to catch any bashism you may have introduced by hand.

References

Frequently Asked Questions