TL;DR
シェルスクリプトの改行コードがUnix形式(LF)ではなく、Windows形式(CRLF)になっています。エラー中の^Mはキャリッジリターン文字(\r)で、/bin/bashの末尾に付着してシェバングが読み取れない状態になっています。以下のコマンドで修正できます:
sed -i '' 's/\r//' script.sh
またはdos2unixがインストール済みの場合:
dos2unix script.sh
修正後、スクリプトを再度実行してください。
実際に何が起きているのか
このエラーが表示された場合:
bash: ./script.sh: /bin/bash^M: bad interpreter: No such file or directory
^Mは謎のフラグではありません。シェバング行の末尾に紛れ込んだキャリッジリターン(\r、ASCII 13)の文字です。macOSから見ると、スクリプトの1行目はこのように見えています:
#!/bin/bash\r\n
カーネルは/bin/bash\rというパスにインタープリターを探そうとします。そのパスは存在しないため、スクリプトは実行されません。
CRLFはWindowsで作成・編集されたスクリプトに紛れ込みます。メモ帳、デフォルトでCRLFが設定されたVS Code、または設定が不適切なWSL上のgitなどが原因で、macOS環境に持ち込まれます。Windowsは行末を\r\nで終端しますが、UnixおよびmacOSは\nのみを期待します。
よくある混入経路は4つあります:
.gitattributesでCRLFチェックアウトが強制されているリポジトリをクローンした場合- USBやファイル共有でWindowsマシンからスクリプトをコピーした場合
- VS Codeの
files.eolが\r\nに設定された状態で編集した場合 - CRLFで保存されたサイトからスクリプトをダウンロードした場合
まず問題を確認する
何かを変更する前に、CRLFが実際に原因かどうかを確認します:
cat -A script.sh | head -5
行末が^M$になっていればCRLFです。クリーンなUnixファイルは$のみ表示されます。
fileコマンドでも平易な英語で確認できます:
file script.sh
# CRLFあり: script.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
# クリーン: script.sh: Bourne-Again shell script, ASCII text executable
バイト単位の詳細確認にはxxdが役立ちます:
xxd script.sh | head -3
CRLFは0d 0aのペアとして表示されます。Unix改行コードは0aのみです。
修正方法
方法1:sed(macOS標準搭載、インストール不要)
sed -i '' 's/\r//' script.sh
-i ''の構文はmacOS固有です。Linux上のGNU sedでは空文字列を省略して-iのみを使います。このコマンドはすべての\rをインプレースで削除します。
方法2:dos2unix(専用ツール)
まずインストールします:
brew install dos2unix
次に変換します:
dos2unix script.sh
ディレクトリ内のシェルスクリプトをまとめて変換する場合は:
find . -name "*.sh" -exec dos2unix {} \;
方法3:tr(もう一つの標準搭載オプション)
tr -d '\r' script_fixed.sh
mv script_fixed.sh script.sh
trはファイルをインプレースで編集できないため、中間ファイルを介した手順が必要です。
方法4:コミット前にVS Codeで修正する
VS Codeの右下隅にあるCRLFをクリックしてLFに切り替え、保存します。すべての新規ファイルでLFをデフォルトにするには、VS Codeの設定に以下を追加します:
{
"files.eol": "\n"
}
方法5:gitレベルで修正する(再発防止)
チェックアウトのたびにCRLFが復活する場合、gitが裏で改行コードを変換しています。これを停止するには:
# このリポジトリのみ
git config core.autocrlf false
# またはグローバル設定
git config --global core.autocrlf input
より恒久的な解決策は、リポジトリ自体に.gitattributesファイルをコミットすることです:
# .gitattributes
*.sh text eol=lf
*.bash text eol=lf
一度コミットすれば、Windows・macOS・Linuxを問わず、すべてのコントリビューターがチェックアウト時にLFのみの改行コードを取得できます。マシンごとの設定は不要です。
修正の確認
先ほどと同じチェックを実行します:
file script.sh
# 表示されるべき内容: ASCII text executable(CRLFの記載なし)
スクリプトに実行権限を付与して実行します:
chmod +x script.sh
./script.sh
/bin/bash^M: bad interpreterエラーが解消されています。
クイックリファレンス
- 症状:エラーメッセージに
^Mが含まれ、スクリプトが実行できない - 原因:WindowsのエディターまたはgitのCRLF改行コード
- 最速の修正方法:
sed -i '' 's/\r//' script.sh(インストール不要) - 最善の長期的解決策:
*.sh text eol=lfを記述した.gitattributes

