改行コードCRLFとLFの違い|Gitの警告とスクリプトが動かない原因
サーバーにアップロードしたスクリプトを実行したら、こう言われた。
bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
あるいは、Gitで何もしていないのに全行が変更扱いになった。あるいは、ファイルを`git add`したらこんな警告が出た。
warning: LF will be replaced by CRLF in config.yml
いずれも原因は同じ、改行コードです。WindowsとLinuxで「改行」の中身が違うことから起きています。
改行に「種類」があるとは、どういうことなのか。
そして厄介なのは、この問題がエラーを出さずに静かに失敗するケースがあることです。順に見ていきます。
改行は「見えない文字」である
Enterキーを押すと行が変わりますが、あそこには何も入っていないように見えて、実際には文字が入っています。使われるのは次の2つです。
| 名前 | 由来 | バイト |
|---|---|---|
| CR(キャリッジリターン) | 復帰 | 0D |
| LF(ラインフィード) | 行送り | 0A |
そして、この組み合わせ方がOSによって違います。「A」「B」という2行を保存したときの中身を比べると、こうなります。
LF (Linux/macOS) 41 0A 42 ← 3バイト
CRLF (Windows) 41 0D 0A 42 ← 4バイト
CR (旧Mac OS) 41 0D 42 ← 3バイト
見た目はどれも同じ2行ですが、ファイルの中身は別物です。1行ごとに1バイトの差が出るため、1万行のファイルなら約10KBの差になります。
なぜ2種類あるのか——タイプライターの名残
そもそもCRとLFという2つの動作は、コンピュータ以前のタイプライターや電動テレタイプに由来します。紙に印字する機械では、次の行に移るために2つの物理動作が必要でした。
- CR(復帰):印字ヘッドを左端まで戻す
- LF(行送り):紙を1行分だけ上に送る
どちらが欠けても正しく次の行になりません。だから制御文字も2つ必要で、機械には「CR + LF」を送っていました。Windowsが今もCRLFを使っているのは、この時代からの互換性を保ち続けているためです。
一方でUnix系は「改行は論理的にはひとつの出来事なのだから、文字も1つでいい」と考え、LFだけに統一しました。機械を動かすための都合をファイルの中身から切り離した、という設計判断です。
OSごとの違い
| 環境 | 改行コード | 備考 |
|---|---|---|
| Windows | CRLF | メモ帳・多くのWindowsソフト |
| Linux | LF | サーバーはほぼこれ |
| macOS(現在) | LF | Unix系のため |
| Mac OS 9以前 | CR | 現在はほぼ使われない |
実務で問題になるのは、ほぼWindowsで作ったファイルをLinuxサーバーに置く場面です。開発は手元のWindows、本番はLinuxという構成が一般的なため、この境界を毎日またぐことになります。
POSIXは「改行は1文字」と定めている
ではどちらが正しいのか。Unix系OSの仕様を定めるPOSIXでは、「行」がこう定義されています。
行(Line):0個以上の改行以外の文字と、それに続く終端の改行文字からなる並び
ここで注目したいのは「終端の(terminating)」という語です。改行は行と行の「区切り」ではなく、行の「終わりを示す印」として定義されています。つまりPOSIXの世界では、最終行にも改行が必要ということになります。
改行で終わっていない末尾については、別の用語が用意されています。
不完全な行(Incomplete Line):ファイル末尾にある、1文字以上の改行以外の文字の並び
Gitの差分表示で末尾に出てくる\ No newline at end of fileという表示は、まさにこれを指しています。エラーではなく「この行は不完全ですよ」という注意書きです。
そしてPOSIXの解説文書は、複数文字を使う方式について踏み込んだ表現をしています。
ここでいう改行は汎用的な行区切りではなく単一の文字である。行末に複数の文字を使うシステムで作られたファイルは、何らかの変換なしには移植性がない
Linuxサーバーの立場からすると、CRLFのファイルは「変換しないと正しく扱えないもの」という位置づけになるわけです。
ただしCRLFが「間違い」なのではない
とはいえ、CRLFが一方的に悪いわけではありません。インターネット上のプロトコルはむしろCRLFを要求します。
電子メールの仕様(RFC 5322)を見ると、ヘッダー行はCRLFで終端すると明記されており、CRLFは「復帰(ASCII 13)の直後に行送り(ASCII 10)が続くもの」と定義されています。HTTPでも同様にCRLFが使われます。
つまりファイルの世界ではLF、通信の世界ではCRLFが標準です。どちらが正しいかではなく、場所によって正解が違うと理解するのが正確です。
【要注意】エラーが出ずに静かに失敗するケース
ここが実務で最も危険な部分です。改行コードの不一致は、必ずしもエラーとして現れません。
WindowsのExcelで作ったCSVをLinux上のプログラムで読む場面を考えます。プログラムがLFだけを目印に行を区切ると、各行の末尾にCRが1文字残ります。
元データ 東京,100
バイト列 E6 9D B1 E4 BA AC 2C 31 30 30 0D 0A
↑CRが残る
最後のセル "100\r" ← 見た目は「100」
問題は、この値が状況によって通ってしまうことです。
| 処理 | 結果 |
|---|---|
| 数値に変換する | 成功する(100として扱われる) |
| 文字列として「100」と比較 | 不一致 |
| 商品コードでデータを突合 | 不一致(該当なしになる) |
| 文字数を数える | 4文字(見た目は3文字) |
数値項目は問題なく通るのに、商品コードや会員IDのような文字列のキーだけが一致しない。しかもエラーは発生せず、「該当データなし」という結果が静かに返ってきます。
金額は合っているのに、なぜか一部のデータだけ取り込まれない——この症状は改行コードを疑う価値があります。
見た目では判別できないため、原因究明に時間がかかりがちなトラブルです。CSVの文字化けと同様、「目に見えない情報がファイルに含まれている」ことが根っこにあります。
スクリプトが動かなくなる仕組み
冒頭のbad interpreterエラーも同じ原理です。シェルスクリプトの1行目には、実行に使うプログラムを書きます。
正常(LF) 23 21 2F 62 69 6E 2F 62 61 73 68 0A
CRLFの場合 23 21 2F 62 69 6E 2F 62 61 73 68 0D 0A
↑余分
LinuxはLFまでを1行目として読むため、実行するプログラムの名前を/bin/bashではなく/bin/bash+CRだと解釈します。そんな名前のプログラムは存在しないので「見つかりません」となる。エラーメッセージに現れる^Mは、このCRの表示形式です。
^Mという表示を見たら改行コードが原因、と覚えておくと診断が早くなります。
Gitでの扱い方
Gitはこの問題を織り込み済みで、変換する仕組みを持っています。Gitの公式ドキュメントによれば、テキストと判定されたファイルは次のように扱われます。
ファイルがインデックスに追加されるとき、改行はLFに正規化される。逆にインデックスから作業ディレクトリにコピーされるとき、設定やプラットフォームに応じてLFからCRLFに変換されることがある
つまりリポジトリの中身は常にLFで、手元での見え方だけを環境に合わせる設計です。この前提を知らないまま設定を変えると、差分が全行変更になる事故が起きます。
設定の選び方
| 設定 | 動作 | 向いている環境 |
|---|---|---|
core.autocrlf=true |
取得時CRLF・保存時LF | Windows |
core.autocrlf=input |
保存時のみLFに変換 | Linux/macOS |
core.autocrlf=false |
変換しない | 後述の方法を使う場合 |
ただしcore.autocrlfは各自のPCの設定なので、メンバーごとにバラバラになりがちです。チーム開発では、リポジトリに.gitattributesファイルを置く方法が確実です。
# 基本はGitに自動判定させる
* text=auto
# シェルスクリプトは必ずLF(Linuxで実行するため)
*.sh text eol=lf
# バッチファイルは必ずCRLF
*.bat text eol=crlf
この方法なら設定がリポジトリに含まれるため、全員に同じルールが適用されます。個人設定に依存しない点が利点です。
確認と変換の方法
改行コードは見えないため、確認には専用の手段が必要です。
- エディタで確認:VS Codeなら画面右下に「LF」「CRLF」と表示され、クリックで変更できます。サクラエディタやメモ帳(Windows 10以降)でも確認できます
- Linux上で確認:
file ファイル名を実行すると、CRLFの場合「with CRLF line terminators」と表示されます - Linux上で変換:
dos2unix ファイル名でLFに変換できます
サーバー上で「動くはずのスクリプトが動かない」ときは、まずfileコマンドで確認するのが手早い方法です。
なお、WindowsとLinuxの境界で食い違うのは改行コードだけではありません。ファイル名の大文字小文字の扱いや使える文字にも差があり、同じように「手元では動くのにサーバーでは動かない」原因になります(ファイル名のルールの違い)。
よくある質問
Q. 改行コードが違うとファイルは壊れますか?
壊れません。データは無傷で、読み取り側の解釈が変わるだけです。変換すれば元通り扱えます。
Q. どちらに統一すべきですか?
サーバーで動かすファイル(シェルスクリプト、設定ファイル、YAMLなど)はLFが安全です。Windows専用のバッチファイルはCRLFが必要です。用途で分けるのが実務的です。
Q. メモ帳で開くと1行に見えるのはなぜですか?
古いWindowsのメモ帳はCRLFしか改行と認識しなかったためです。Windows 10以降のメモ帳はLFにも対応しており、この症状は起きにくくなっています。
Q. なぜ差分が全行変更になるのですか?
改行コードを一括変換すると、Gitからは全行のバイト列が変わったように見えるためです。内容が同じでも全行が変更扱いになります。.gitattributesを最初に決めておくと防げます。
Q. 「^M」とは何ですか?
CR(0D)を画面上で表示するときの表記です。この文字が見えたら、CRLFのファイルがLFの環境で読まれていると判断できます。
参考にした主な調査・資料
- The Open Group Base Specifications: Definitions(The Open Group / IEEE)— POSIXにおける「行」「不完全な行」「テキストファイル」の定義
- gitattributes ドキュメント(Git)— text属性・eol属性による改行コード変換の仕様
- RFC 5322: Internet Message Format(IETF)— メールにおけるCRLFの定義と使用
まとめ
改行コードの問題は、見えない文字が引き起こすトラブルの典型です。
- 改行は文字であり、LF(0A)とCRLF(0D 0A)で中身が違う
- 2種類あるのはタイプライターの物理動作の名残
- POSIXは改行を単一の文字と定義し、複数文字方式は移植性がないとする
- ただしメールやHTTPはCRLFを要求する。場所によって正解が違う
- 最も危険なのはエラーが出ない失敗。数値は通るのに文字列の突合だけ一致しない
^Mが見えたら改行コードを疑う- チームでは
.gitattributesでルールを固定する
「文字化けはしていないのに、一部のデータだけ処理されない」——そんなときに改行コードを思い出せるかどうかで、原因究明の速さが変わります。