Skip to content
Back to skills

Shell Portability

ASecurity

Write shell that runs across POSIX sh, bash versions, and GNU/BSD userlands, or decide deliberately not to. Use when scripts must run on multiple platforms, containers, or CI images.

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 5, 2026
ai-agentspythonrustgoshellbashgit

Works with

  • cli

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add Amey-Thakur/AI-SKILLS --skill shell-portability --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Shell Portability?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Shell Portability
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/amey-thakur-shell-portability/badge)](https://www.skillsdirectory.com/skills/amey-thakur-shell-portability)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: shell-portability
description: Write shell that runs across POSIX sh, bash versions, and GNU/BSD userlands, or decide deliberately not to. Use when scripts must run on multiple platforms, containers, or CI images.
---

# Shell portability

Portability is a decision, not a default. Declare which environments
you support, write to that contract, and test on it; the alternative
is scripts that work on your laptop and fail in the alpine container
at 2am.

## Method

1. **Declare the dialect in the shebang and mean it.**
   `#!/bin/sh` promises POSIX: no arrays, no `[[`, no `local`
   (strictly), no `pipefail`, no process substitution. `#!/usr/bin/
   env bash` buys those features but must find bash: not guaranteed
   on alpine/BSD/embedded images. Choosing bash-everywhere and
   installing it in your images is a legitimate answer; pretending
   `sh` while writing bashisms is not: `checkbashisms` and
   shellcheck's sh mode catch the drift (see bash-robustness).
2. **Know the macOS reality.** macOS ships a 2007 bash 3.2
   (no associative arrays, no `mapfile`) and BSD userland; dev
   machines are the most common portability break. Either target
   bash 3.2 + BSD tools, or make the script's first act check for
   and prefer brew-installed GNU tools/newer bash: explicitly.
3. **Write to the POSIX core of the classic tools.** GNU and BSD
   diverge on flags: `sed -i` (needs `-i ''` on BSD), `date -d`
   vs `-v`, `grep -P` (GNU only), `xargs -d`, `readlink -f`
   (absent on older BSD/macOS). Portable moves: sed to a temp file
   and move (see script-idempotency), `awk` for date/field math,
   `grep -E` only, `find ... -exec` over xargs extensions. When a
   GNU-ism saves real complexity, require GNU explicitly
   (`command -v gsed`) rather than half-working.
4. **Probe features, not platforms.** `command -v tool`,
   `if some_feature_test; then` at startup, with clear failure
   messages naming what to install; branching on `uname` strings
   accumulates special cases that rot (feature detection is the
   same discipline as browser-matrix, aimed at userlands).
5. **Test on the actual matrix in CI.** A container job per
   supported environment (debian, alpine/busybox, macOS runner)
   running the script's test invocations (see
   test-environment-parity); portability claims without CI
   coverage are folklore. Pin the images so the matrix is
   versioned like any dependency.
6. **Escalate to a portable runtime when the matrix hurts.**
   Two or more of: associative data, JSON, retries with backoff,
   Windows support: switch to Python (stdlib-only for
   portability; see python-cli-tools) or ship a static binary
   (Go/Rust: see go-project-layout). The portability tax on shell
   grows superlinearly with script complexity; pay it in a
   language designed for it (see bash-robustness step 6).

## Boundaries

- Windows is not a shell-portability target; WSL/git-bash
  availability is deployment-specific. Cross-platform automation
  for Windows means PowerShell (see powershell-essentials) or a
  compiled tool, decided upfront.
- Containers you fully control collapse the problem: pin one
  image, write to it, and stop paying the tax; portability
  matters at the edges you do not control (dev machines, customer
  environments, heterogeneous CI).
- Locale and encoding vary too (`LC_ALL=C` for stable sort/regex
  behavior in pipelines); set it explicitly in scripts that parse
  or sort text (see text-processing).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…