CRLF と LF CIを壊すラインエンドのバグ
あなたのシェルスクリプトはローカル環境で動作するが、CI環境では不明なインタープリターエラーが発生する。原因は通常、CRLFの行終端である。CRLFとは何か、なぜLinuxパイプラインで問題を起こすのか、そして.gitattributesとエディタの設定を使って問題を永続的に解決する方法を学ぼう。
あなたのシェルスクリプトはラップトップでうまく動作しました。GitHubにプッシュすると、CIがそれを拾い上げ、パイプラインが暗黙のエラーで終了します——たとえば /bin/bash^M: bad interpreter または、明らかな理由がないのに終了コード126を返すジョブ。スクリプトの文法は正しいです。1行も変更していません。しかし、ビルドは破綻しています。
九割九分、原因は改行文字です。具体的には、Linuxが見つけるべきだけがLFであるファイルの中に隠れているWindowsスタイルのCRLF改行文字です。この記事では、その2つの文字が実際に何を意味するか、その違いが開発者たちが実際には過小評価している理由、そして問題を根本的に解決する方法を説明します。
CRLFとLFが実際に意味するもの
すべてのテキストファイルには、1行の終わりを示す方法が必要です。異なるオペレーティングシステムで使われている2つのコントロール文字があります:
- LF (改行(Line Feed、
\n、16進数0x0A) — UnixおよびLinuxの標準。macOSはOS X以降これを使用しています。1バイトごとに1行の終わりを示します。 - CRLF (カーソル戻し+改行(Carriage Return + Line Feed、
\r\n、16進数0x0D 0x0A) — Windowsの標準で、DOSから継承され、それらからテレタイプ機から継承されています。2バイトごとに1行の終わりを示します。
名前は古い打字機の物理的な動作から来ています。カーソル戻しは印刷ヘッドをラインの開始位置に戻し、改行は紙を1行上に巻き上げました。DOSはこの2つの動作を正確にエンコードし、Unixは最小のアプローチを選択し、改行だけを残しました。
なぜCIが壊れ、ラップトップでは動くのか
Windowsでコードを書く場合、編集者がCRLFでファイルを保存すると、ローカル環境ではすべてが正常に動作します——Windowsのツールは両方の形式を透明に扱います。しかし、あなたのCIパイプラインはほぼ確実にLinux上で実行され、Linuxシェルインタープリターやは \r を印刷可能な文字として扱います。bashがスクリプトの行末に \r\nを見ると、コマンドに追加のカーソル戻しを含むと認識します。結果は次のようになります:
/bin/bash^M: bad interpreter: No such file or directory
の ^M そのエラーは、ターミナルが \rをどのようにレンダリングするかに似ています。bashは、カーソル戻しを含むシェルバーンを実行しようとしており、そのパスにインテリプリタが存在しません。
同じ問題は、より微妙な形で現れます:
- 環境変数の値に追加の
\r文字が、文字列比較を静かに破壊します。 - Docker
ENTRYPOINTスクリプトがシェルバーンが破損しているため、開始できません。 - Pythonスクリプトが設定ファイルを読み取り、キーが
"setting\r"の代わりに"setting". - となる。
\r.
Makefileがタブ文字が
に続くためルールを無視します。
問題を検出する方法
cat -A deploy.sh
何らかの修正を行う前に、ファイルが実際にCRLFの終端を持っていることを確認してください。いくつかの簡単な方法があります: ^M$ cat -A (Linux/macOS) $.
CRLFで終了する行は
file deploy.sh
# deploy.sh: Bash script, ASCII text, with CRLF line terminators
を終端に表示します。LFで終了する行はのみ
xxd deploy.sh | grep "0d 0a"
file コマンド 0a xxd または hexdump
の一致が確認されればCRLFです。もし行終端にのみ
が見られるなら、問題はありません。
問題を解決する方法
# Install
sudo apt install dos2unix # Debian/Ubuntu
brew install dos2unix # macOS
# Convert a single file
dos2unix deploy.sh
# Convert all shell scripts in the repo
find . -name "*.sh" -exec dos2unix {} \;
dos2unix(最も速い解決策)
sed -i "s/\r//" deploy.sh
1回インストールし、すべてのファイルに適用:
tr -d "\r" < deploy.sh > deploy_fixed.sh && mv deploy_fixed.sh deploy.sh
sed(追加ツールを必要としない)
tr(POSIX対応)
問題を防ぐ:Git設定
あ .gitattributes ファイルを修正するのは反応的な対応です。持続的な解決策は、Gitがどのように行終端を処理するかを指示することです。その問題がリポジトリに到達しないようにします。
# Normalize all text files to LF in the repo
* text=auto eol=lf
# Explicitly enforce LF for files that must never have CRLF
*.sh text eol=lf
*.bash text eol=lf
Makefile text eol=lf
Dockerfile text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.json text eol=lf
# Binary files — do not touch
*.png binary
*.jpg binary
*.gif binary
*.zip binary
*.gz binary
の text=auto eol=lf .gitattributesアプローチ(推奨)
リポジトリにコミットされたファイルは、すべての開発者が自分のローカルGit設定に関係なく、一貫した行終端を確保します。リポジトリのルートに次の内容を追加してください: .gitattributeslineはGitがテキストファイルを自動検出し、開発者のOSに関係なくLFで保存するように指示します。以下の明示的な行は、CRLFが実行時に失敗を引き起こすファイルをロックダウンします。
git add --renormalize .
git commit -m "normalize line endings"
を追加または変更した後、リポジトリ内の既存ファイルをリノーマライズしてください:
Gitは、チェックアウトおよびコミット時に行終端変換を制御するグローバル設定 core.autocrlf を持っています。推奨される値はOSに依存します:
# Windows — convert CRLF to LF on commit, LF to CRLF on checkout
git config --global core.autocrlf true
# Linux/macOS — convert CRLF to LF on commit, no conversion on checkout
git config --global core.autocrlf input
を依存する問題:それは設定されたマシンでのみ適用されます。設定していない貢献者がCRLFファイルをプッシュできます。ファイルはリポジトリ自体によって強制されるため、厳密に信頼性があります。 core.autocrlf エディタ設定を正しく設定する .gitattributes エディタは最初の防衛線です。いくつかの簡単な設定があります:
現在のファイルの行終端はステータスバー(右下)に表示されます。それをクリックしてCRLFとLFの間を切り替えます。新しいファイルのデフォルトを設定するには、これを
に追加してください。
はい(組み込み)
JetBrains IDEs(IntelliJ、WebStorm、PyCharm) settings.json:
{
"files.eol": "\n"
}
に移動してください
Settings → Editor → Code Style および Line separator Unix and macOS (\n) に を設定してください。また、リポジトリのルートにファイルを追加できます: .editorconfig ほとんどの現代のエディタは
[*]
end_of_line = lf
insert_final_newline = true
をネイティブまたはプラグイン経由で尊重します。リポジトリにコミットすることで、貢献者は手動設定なしで一貫したデフォルトを取得できます。 .editorconfig 完全なチェックリスト
行終端問題があるリポジトリを標準化する場合、次の順番で進めます:
を追加し、シェルスクリプト、Dockerファイル、設定ファイルに対して明示的なルールを設定します。
- 新しい
.gitattributesを追加し、既存のトラッキングファイルを修正します。* text=auto eol=lfを実行して、Linux上で実行される未トラッキングスクリプトを修正します。 - Linux/macOSマシンで
.editorconfigを追加し、既存のトラッキングファイルを修正します。end_of_line = lf. - でWindows上で
git add --renormalize . && git commit -m "normalize line endings"CIのリントステップ——たとえば - でWindows上で
dos2unix——を追加し、デプロイ前に再発を検出します。 - セット
core.autocrlf inputLinux/macOSマシンでグローバルに;trueWindowsで。 - CIのlintステップを追加する——例えば
grep -rUl $'\r' *.sh——を実行して、デプロイに達する前にどのようなリグレッションがあるかを検出する。
あなたも好きかもしれません
恵 スコアボードが到着しました!
スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!
