
Portability is less about avoiding Bash and more about making the boundary visible. A script should declare its interpreter, quote data, check commands, and fail in a way that leaves useful evidence.
Declare the interpreter
Use a shebang that matches the language you are writing:
#!/bin/sh
set -eu
If the script needs Bash arrays, [[ ... ]], or mapfile, say so with
#!/usr/bin/env bash instead of accidentally depending on the caller’s shell.
Quote data and check tools
Treat paths and user input as data, even when they look harmless:
if ! command -v rsync >/dev/null 2>&1; then
printf '%s\n' "rsync is required" >&2
exit 127
fi
for path in "$@"; do
printf 'checking %s\n' "$path"
done
The explicit check produces a useful error in a minimal container rather than a cryptic “not found” several lines later.
Test failure as a first-class path
Run scripts with empty input, spaces in filenames, a missing dependency, and a command that returns non-zero. A short script that behaves predictably in those cases is more portable than a clever one that only works on one workstation.

