Bash versus POSIX sh and Portability Tradeoffs
Portability is a runtime requirement, not a style contest. Bash can dramatically simplify robust scripts when it is available; POSIX sh provides a smaller common denominator when environments are constrained.
Learning objectives
By the end of this lesson
- Identify major Bash-only features.
- Match shebang to the language used.
- Separate shell portability from utility portability.
- Define supported platforms and versions.
- Prefer capability detection where useful.
1. Portability is a design target, not a moral preference
POSIX sh targets a smaller common shell language. Bash adds arrays, [[ ]], process substitution, associative maps, extended parameter expansion, and many conveniences. Choose based on deployment constraints.
flowchart TD
R["runtime environments"] --> Q{"Bash guaranteed?"}
Q -->|"yes"| B["use Bash features deliberately"]
Q -->|"no"| P["target POSIX sh subset"]2. The shebang must match the language you wrote
#!/usr/bin/env bash
# Bash script
#!/bin/sh
# POSIX-sh-targeted scriptDo not write Bash arrays under #!/bin/sh and expect consistent behavior.
3. [[ ]] is Bash-specific; [ ] is broadly portable
# Bash:
if [[ $name == api-* ]]; then
printf 'match\n'
fi
# POSIX-style:
case $name in
api-*) printf 'match\n' ;;
esac4. Arrays are a major portability boundary
# Bash:
hosts=("web 1" "web2")
for host in "${hosts[@]}"; do
printf '%s\n' "$host"
donePOSIX sh has positional parameters but no standard indexed-array type. If the data model needs arrays, Bash may be the clearer dependency.
5. Process substitution is Bash-specific
# Bash:
while IFS= read -r line; do
printf '%s\n' "$line"
done < <(generate_data)Portable sh often uses a pipeline or temporary file instead, but that can change variable-scope behavior.
6. source is Bash; dot is portable
# Bash:
source ./lib.sh
# POSIX:
. ./lib.sh7. Prefer portable primitives even inside Bash when convenient
printf, case, simple functions, and direct argument passing are portable and predictable. Using Bash does not require maximizing Bash-specific syntax.
8. Shell portability and utility portability are separate
A script can use POSIX shell syntax but still depend on GNU-only sed, date, xargs, or find flags. Portability includes the external toolchain.
9. macOS, Linux, BusyBox, WSL, and Git Bash differ
Even when Bash exists, version and bundled utilities vary. macOS historically ships an older system Bash; minimal containers may use BusyBox tools; Git Bash adds a Unix-like layer over Windows. Define and test the platforms you claim to support.
10. Feature checks can be more useful than OS-name checks
if help wait 2>/dev/null | grep -q -- '-n'; then
have_wait_n=true
else
have_wait_n=false
fiDetect the capability your script needs rather than assuming every version of an OS provides it.
11. Hands-on lab: identify Bash-only constructs
mkdir -p "$HOME/devops-academy/bash/chapter18/lesson04"
cd "$HOME/devops-academy/bash/chapter18/lesson04"
cat > bash-only.sh <<'EOF'
#!/usr/bin/env bash
set -u
items=("one" "two words")
for item in "${items[@]}"; do
if [[ $item == *words ]]; then
printf 'matched=%s\n' "$item"
fi
done
EOF
cat > portable-style.sh <<'EOF'
#!/bin/sh
for item in one two three; do
case $item in
two) printf 'matched=%s\n' "$item" ;;
esac
done
EOF
chmod u+x bash-only.sh portable-style.sh
bash bash-only.sh
sh portable-style.shVerification checklist
12. Knowledge check
Question 1. Are Bash arrays part of POSIX sh?
Question 2. Is source portable POSIX syntax?
Question 3. Does POSIX shell syntax guarantee utility portability?
Question 4. What is often better than checking the OS name?
13. Summary
Choose Bash when its features reduce complexity and Bash is a valid runtime dependency. Choose POSIX sh when the deployment environment requires a smaller common denominator. In either case, test the external utilities and versions in your support matrix.
14. Further reading
- GNU Bash Reference Manual.
- POSIX Shell Command Language specification.
- ShellCheck portability diagnostics.
- BusyBox and platform-specific utility documentation.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.