Bashで「bad substitution」エラーが出る原因と対処法

シェルスクリプトを書いていると、次のようなエラーに遭遇することがあります。

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

一見すると単純な文法ミスのように見えますが、これは シェルの種類や互換性の問題 が原因であることが多いです。

本記事では、このエラーの原因、再現例、Bash と sh/zsh との違い、対処法、実運用での注意点を詳しく解説します。

1. エラーの背景と原因

「bad substitution」は、シェルが変数展開構文を理解できないときに発生します。特に ${VAR//pattern/replacement} という「全置換」構文は POSIX 標準ではなく、Bash 独自機能 です。そのため、シェバンが /bin/sh だったり、zsh で互換モードが有効だったりするとエラーになります。

また、CI/CD 環境や Docker コンテナでは /bin/sh が dash であることが多く、ローカルでは動いていたスクリプトが本番で突然壊れるといったケースもあります。

2. 再現例と実際のエラーメッセージ

再現は簡単で、/bin/sh で実行すると次のようになります。

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

実行結果:

./sample.sh: 3: Bad substitution

一方で /bin/bash で実行すると正常に動作します。

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

つまり、どのシェルでスクリプトを実行しているかが重要 です。

3. 対処法1: シェバンをBashに変更

最もシンプルな解決策は、スクリプトの先頭行を Bash に変更することです。

#!/usr/bin/env bash

この方法なら、POSIX sh ではサポートされない拡張構文も問題なく動作します。ただし、システムに Bash がインストールされていない環境(軽量 Alpine Linux など)では別途インストールが必要です。

4. 対処法2: POSIX 互換構文に書き換える

Bash を使えない場合、sedparameter expansion の他の形を使って置換を実現します。

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

この方法は POSIX 互換のため、sh でも zsh でも動作します。パフォーマンスは若干落ちますが、ポータビリティを優先したいスクリプトではこちらが安全です。

5. 実運用での注意点

チーム開発や CI/CD パイプラインでは、シェルが統一されていないと再現困難なバグが発生します。次の点を意識するとトラブルを防げます。

  • すべてのスクリプトで #!/usr/bin/env bash#!/bin/sh を明示
  • CI 環境のシェルをドキュメント化
  • Dockerfile 内で RUN apk add bash などを行い Bash を確実に利用可能にする
  • shellcheck を利用して静的解析し、非互換構文を事前に検知

6. まとめ

「bad substitution」エラーは単なるスペルミスではなく、実行シェルが構文を理解できないことが原因 です。Bash 特有の構文を使っている場合は、シェバンを Bash にするか POSIX 互換構文に書き換えることで解決できます。実運用ではシェルの種類を統一し、CI/CD 環境とローカルで挙動が揃うようにすることが重要です。

参考

タイトルとURLをコピーしました