広告が嫌いですか? 行く 広告なし 今日

CRLF と LF CIを壊すラインエンドのバグ

掲載日

あなたのシェルスクリプトはローカルで動作するが、CI環境では「bad interpreter: /bin/bash^M」とエラーが発生する。これはCRLFの行終端による問題である。その原因、検出方法、そして永続的に解決するための.gitattributesの使用方法を学ぼう。

CRLF と LF:CIを壊すラインエンドのバグ 1

あなたのシェルスクリプトはローカルのコンピュータでうまく動きます。GitHubにプッシュすると、CIが暗黙的なエラーを表示します。 /bin/bash^M: bad interpreter あなたはソフトウェア開発における最も見えないバグに襲われました:誤ったラインエンドです。

このガイドでは、CRLFとLFが実際に何であるか、それらを混ぜることでスクリプトや設定ファイルが静かに腐食する理由、そしてラインエンドのバグがパイプラインに到達するのを防ぐための正確なステップを説明します。

CRLFとLFとは何か?

すべてのテキストファイルには、行の終わりを示す方法が必要です。これは、物理的なテレタイプ機の時代から継承された2つの規則があります:

  • LF(ラインフィード) — 1つの \n 文字(バイト 0x0A)。Linux、macOS、およびすべてのUnix系システムで使用されています。
  • CRLF(カーソルリターン + ラインフィード) — 2つの文字、 \r\n (バイト 0x0D 0x0A)。WindowsおよびMS-DOSで使用されています。

名前はタイプライターの仕組みから来ています。カーソルリターンは印刷ヘッドをラインの開始位置に戻し、ラインフィードは紙を1行進めていました。Windowsは両方を保持し、Unixは冗長なカーソルリターンを削除しました。 カーソルリターン は、 ラインフィード は、紙を1行進めるもので、Windowsは両方を保持し、Unixは冗長なカーソルリターンを削除しました。

CRLFがCIパイプラインを壊す理由

Linux(ほぼすべてのCIランナーが実行される環境)では、 \r は空白文字ではありません — それは文字としての実体です。シェルスクリプトがCRLFエンドで保存された場合、各行の終わりに \r が含まれます。カーネルはシェルの最初の行を #!/bin/bash\r と見なし、正確に名前が bash\rであるバイナリを探しています。そのバイナリは存在しません。

結果として発生するエラーは次のようになります:

: bad interpreter: /bin/bash^M: No such file or directory

^M は、ターミナルがカーソルリターン文字を表示する方法です。ほとんどのテキストエディタでは見えないため、このバグは非常に混乱を引き起こします。

CRLFが静かにダメージを与える他の場所

  • Dockerfile — CRLFが含まれる RUN インストラクションは、すべてのコマンドに \r を注入し、文字列比較やファイルパスを破壊します。
  • PythonスクリプトSyntaxError: unexpected character after line continuation character において \\r\n.
  • .envファイル — 環境変数の値に \rが追加され、 APP_ENV=production\r は期待される production.
  • CSVおよびデータファイル — 行ごとに読み込むパーサーは、すべての行の最後のフィールドに \r を含めます。
  • SSH authorized_keys — CRLFでエンコードされたキーファイルは、SSHデーモンによって静かに拒否されます。
  • Git diff — すべての行が変更されているように見え、実際の変更をノイズの中に埋めてしまいます。

ラインエンドの問題を検出する方法

ほとんどのエディタはデフォルトでラインエンドを隠します。信頼できるチェック方法は次の通りです:

catまたはhexdumpを使用

# Show ^M characters
cat -A yourfile.sh | head -5

# Hex dump to see 0x0d (CR) characters
hexdump -C yourfile.sh | head -10

fileコマンドを使用

file yourfile.sh
# CRLF output: yourfile.sh: ASCII text, with CRLF line terminators
# LF output:   yourfile.sh: ASCII text

grepを使用

# Returns exit code 0 (found) if CRLF endings exist
grep -rlP "\r" . --include="*.sh" --include="*.py" --include="*.yml"

CRLFラインエンドの問題を修正する方法

オプション1: dos2unix(最も速い一時的な解決策)

dos2unix ファイルからカーソルリターンを削除します。これはすべての主要なLinuxディストリビューションで利用可能です:

# Fix a single file
dos2unix yourscript.sh

# Fix all shell scripts recursively
find . -name "*.sh" -exec dos2unix {} \;

# Reverse: convert LF to CRLF (unix2dos)
unix2dos yourfile.sh

オプション2: sed(追加ツールを必要としない)

# Remove carriage returns in-place
sed -i 's/\r//' yourscript.sh

# Or using tr
tr -d '\r' < input.sh > output.sh

オプション3: VS CodeまたはJetBrainsで修正

VS Codeでは、ラインエンドモードはステータスバー(右下)に表示されます。現在のファイルのCRLFとLFの切り替えをクリックできます。新しいファイルのデフォルトを変更するには、 "files.eol": "\n" を設定します。 settings.json.

JetBrains IDEでは、 File → Line Separators で現在のファイルを変更、または Editor → Code Style → Line separator.

でデフォルトを設定できます。

正しい解決策:.gitattributesファイル .gitattributes 一時的な解決策は拡張できません。正しい解決策は、Gitがどのラインエンドを強制するかを明確に示す

# .gitattributes — commit this to the root of your repository

# Default: normalize all text files to LF in the repo
* text=auto eol=lf

# Explicitly enforce LF for scripts and configs
*.sh    text eol=lf
*.bash  text eol=lf
*.py    text eol=lf
*.rb    text eol=lf
*.yml   text eol=lf
*.yaml  text eol=lf
*.json  text eol=lf
*.env   text eol=lf
Dockerfile text eol=lf
Makefile   text eol=lf

# Windows-only files can keep CRLF
*.bat  text eol=crlf
*.cmd  text eol=crlf
*.ps1  text eol=crlf

# Binary files — never touch line endings
*.png  binary
*.jpg  binary
*.gif  binary
*.zip  binary
*.pdf  binary

ファイルです。このファイルを追加した後、すべてのリポジトリを一度に正常化するために次のコマンドを実行します:

git add --renormalize .
git commit -m "chore: normalize line endings via .gitattributes"

Gitのautocrlf設定 — そしてなぜそれがよくないことが多いか

Gitは、ラインエンドを自動的に変換するための core.autocrlf 設定を持っています:

  • core.autocrlf=true — チェックアウト時にLFをCRLFに変換(Windows)、コミット時にCRLFをLFに変換。Windowsユーザー向けに意図されています。
  • core.autocrlf=input — コミット時にCRLFをLFに変換し、チェックアウト時には何もしません。Mac/Linuxユーザー向けに安全です。
  • core.autocrlf=false — Gitは何もしません。エディタが保存した内容がそのままコミットされます。

問題: core.autocrlfローカル 設定で保存され、チーム内のすべての開発者が異なる値を持っているため、異なるマシンからコミットされたファイルは異なるラインエンドを生成します。これにより、diffに常にノイズが発生し、誰が最後にファイルを編集したかによってCIの失敗が間歇的に発生します。 ~/.gitconfig. チーム内のすべての開発者は異なる価値を持ち、異なるマシンからのコミットが異なる行終端を生成します。これにより、差分に常にノイズが生じ、ファイルを最後に編集した人が異なる場合にCIの失敗が間歇的に発生します。

ルールとして: をリポジトリにラインエンドポリシーとして設定してください。そして .gitattributes を各開発者が持つ値に設定して、 core.autocrlf がそれを上書きします。 .gitattributes CIのチェックを追加する

.gitattributesが存在する場合でも、CIに明示的なチェックを追加することは価値があります。2行のステップでほとんどのケースをカバーできます:

このステップは、元の場所であるPRで明確に失敗するのではなく、デプロイ時に静かに失敗します。 .gitattributes CRLFとLFの迅速な参照

# In your CI workflow (GitHub Actions example)
- name: Check for CRLF line endings
  run: |
    if grep -rlP "\r" . --include="*.sh" --include="*.py" --include="*.yml" --include="Dockerfile"; then
      echo "ERROR: CRLF line endings found. Run dos2unix on the above files."
      exit 1
    fi

LF(

CRLF(

バイト\n)使用\r\n)
Linux、macOS、Unix0x0A0x0D 0x0A
Windows、MS-DOSシェルスクリプトに安全いいえ — シェルの最初の行を破壊
Dockerfileに安全はい.envファイルに安全
いいえ — 値に追加された\rを含むはいいいえ
Gitの推奨はいリポジトリ内でLFに正常化
.bat/.cmd/.ps1ファイルにのみCRLFとLF:CIを壊すラインエンドのバグ2CRLFとLF:CIを壊すラインエンドのバグ1
広告なしで楽しみたいですか? 今すぐ広告なしで

拡張機能をインストールする

お気に入りのブラウザにIOツールを追加して、すぐにアクセスし、検索を高速化します。

に追加 Chrome拡張機能 に追加 エッジ拡張 に追加 Firefox 拡張機能 に追加 Opera 拡張機能

スコアボードが到着しました!

スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!

ニュースコーナー 技術ハイライト付き

参加する

価値ある無料ツールの提供を継続するためにご協力ください

コーヒーを買って