Public

How to Fix "bad substitution" in Bash

This article contains ads and affiliate links.

Linux/Bash/Shell Linux
t-salad t-salad committed bb02aed

While writing a shell script, you may run into this error:

Sponsored Links
./script.sh: line 10: ${VAR//foo/bar}: bad substitution

It looks like a simple typo, but it is almost always a shell compatibility problem: the shell running your script does not understand a Bash-specific parameter expansion. This article covers the cause, a reproduction, the difference between Bash and sh/zsh, and two ways to fix it.

Cause

The bad substitution error appears when the shell cannot parse a parameter expansion. Syntax like ${VAR//pattern/replacement} (global replace) is not POSIX; it is a Bash extension. So it fails when the shebang is /bin/sh, or when zsh runs in a compatibility mode.

This bites hardest in CI/CD pipelines and Docker containers, where /bin/sh is often dash. A script that works locally in Bash suddenly breaks in production.

Reproduction

Run this with /bin/sh and it fails:

#!/bin/sh
VAR="foo-bar-foo"
echo ${VAR//foo/baz}
./sample.sh: 3: Bad substitution

The same script works with /bin/bash:

#!/bin/bash
VAR="foo-bar-foo"
echo ${VAR//foo/baz}
# => baz-bar-baz

Fix 1: Change the shebang to Bash

The simplest fix is to make the script run under Bash:

#!/usr/bin/env bash

This lets you use non-POSIX expansions safely. Note that on minimal images (such as Alpine Linux) Bash may not be installed, so you may need to add it (apk add bash).

Sponsored Links

Fix 2: Rewrite with POSIX-compatible syntax

If you cannot rely on Bash, use sed or another POSIX form to do the replacement:

#!/bin/sh
VAR="foo-bar-foo"
VAR=$(echo "$VAR" | sed 's/foo/baz/g')
echo "$VAR"

This runs under sh and zsh as well. It is slightly slower, but it is the safe choice when portability matters.

Avoiding it in practice

  • Declare the shell explicitly in every script: #!/usr/bin/env bash or #!/bin/sh.
  • Document which shell your CI environment uses.
  • Install Bash in your Dockerfile (RUN apk add bash) if your scripts need it.
  • Run shellcheck to detect non-portable syntax before it reaches CI.

Summary

bad substitution is not a typo; it means the shell running the script cannot understand the expansion. Use a Bash shebang, or rewrite the expansion in POSIX-compatible form. In production, keep the shell consistent between local and CI so behavior matches.

References

Sponsored Links
Sponsored Links
★ Share on X
Related posts