45395 - シコウサクゴ -

CRLFな設定ファイルはgrepを黙って敗北させる:判定はVCS専用コマンドをSoTにする

2026-06-14
AI駆動開発
AI駆動開発
Claude Code
Git
CRLF
検証
Last updated:2026-06-14
11 Minutes
2133 Words

設計ドラフトをレビューしていて、「あるディレクトリが ignore 設定に登録済みかどうか」を確認しようとしました。単純な確認のはずが、使うコマンドによって結論が真逆になり、危うく誤った判断をするところでした。

犯人は CRLF 改行でした。grep はエラーを出さず、ただ静かに「ヒット0」を返す。その空結果を AI も人も「登録されていない」と解釈してしまう。テキストツールが黙って敗北していたのです。

本記事は、この経験から「存在・追跡・無視の判定は、テキスト grep ではなく VCS 専用コマンドを唯一の真実(SoT)にする」という運用にした記録です。

事の発端:grep と check-ignore が矛盾した

確認したかったのは、ある無視設定(gitignore)に .claude/worktrees/ のようなディレクトリのエントリが登録済みかどうか、でした。

まず素直に grep を打ちました。

1
$ grep "worktree" .gitignore
2
(ヒット0)

ヒット0です。普通に読めば「登録されていない」と判断するところです。ところが念のため git check-ignore を末尾スラッシュ付きで試すと、別の答えが返ってきました。

1
$ git check-ignore -v ".claude/worktrees/"
2
.gitignore:103

.gitignore の103行目を指して「登録済み」のように見えます。grep は「無い」と言い、check-ignore は「103行目にある」と言う。二つのツールが正面から矛盾しました。

原因究明:CRLF と末尾スラッシュ、二重の罠

矛盾の原因を追っていくと、罠が二重に仕掛けられていました。

罠1:CRLF 改行が grep の行判定を狂わせる

その .gitignoreCRLF 改行(\r\n で保存されていました。awk / sed / grep といったテキストツールは、行末の \r の存在を前提していないので、行のカウントや一致判定が \r の影響で混乱します。指された103行目を実際に見ると、そこは「\r だけが残った空行」でした。エントリが書かれた行ですらなかったのです。

CRLF の厄介なところは、ツールがエラーを出さないことです。エンコーディングや改行の不整合があっても、grep は「異常です」とは言わず、ただ「マッチしませんでした」という空結果を返します。空結果は、本当に無いのか、ツールが取りこぼしたのか、出力からは区別がつきません。

罠2:check-ignore の末尾スラッシュ出力が誤解を招く

もう一つの罠が git check-ignore の側にありました。-v(verbose)を付けて末尾スラッシュ付きのディレクトリ形式を渡すと .gitignore:103 を返すのですが、これが誤解を招く出力でした。

そこで検証方法を変えてみました。

1
$ git check-ignore -n ".claude/worktrees/anything"
2
:: (マッチなし=実際には ignore されていない)
3
4
$ git show HEAD:.gitignore | grep worktree
5
(ヒット0)

-n(non-matching を表示)で問い直すと、返ってきたのは ::、つまり実際には ignore されていないという答えでした。さらにコミット版を git show HEAD:.gitignore で取り出して grep しても、ヒット0。コミットされた版にもエントリは無かったのです。

最終的な結論:実際には ignore されていなかった

複数の検証方法を突き合わせた結果、正しい結論は「実際には ignore されていない」でした。各コマンドの言い分を整理すると、こうなります。

コマンド出力解釈
grep "worktree" .gitignoreヒット0CRLF で黙って失敗。空結果=結論不明
git check-ignore -v "...worktrees/".gitignore:103末尾スラッシュの罠。103行は空行
git check-ignore -n "...worktrees/anything"::ignore されていない(信頼できる)
git show HEAD:.gitignore | grep worktreeヒット0コミット版にエントリ無し(裏取り)

最初の grep も、check-ignore -v の末尾スラッシュ出力も、どちらも誤った方向へ導きました。決め手になったのは git check-ignore -n(非マッチ表示)と git show HEAD: という、VCS 専用の SoT コマンドでした。

学び:判定は VCS 専用コマンドを真実とする

この一件から得た学びは、はっきりしています。

AI にテキスト処理スクリプトを生成させて「ファイルにこの文字列があるか」を判定させるとき、対象が CRLF やエンコーディングの差を含むと、grep / awk / sedエラーを出さず黙って空結果を返します。そして AI も人も、その空結果を「存在しない」と早合点します。

特に次の種類の判定は、テキスト grep に任せてはいけません。

  • 無視判定(ignore されているか)→ git check-ignore
  • 追跡判定(追跡対象か)→ git ls-files
  • コミット版の内容確認(コミットに含まれるか)→ git show HEAD:

これらは Git が状態を正確に知っている領域です。テキストとしてファイルを舐めるのではなく、Git に直接問い合わせるほうが、CRLF や空白の揺れに惑わされません。

ただし、同じ Git コマンドでも出力形式の罠が残ります。今回の check-ignore のように、末尾スラッシュ付き / -n / --no-index で結果が変わることがあります。だから結論は、複数の検証方法で裏取りするのが鉄則です。

パターン化:『空結果=存在しない』と即断しない

この事故を一般化すると、核心は「空結果を結論にしない」という一点に尽きます。

ツールが空(ヒット0)を返したとき、それは「存在しない」ではなく「このツールでは見つけられなかった」を意味する。エラーが出ない空結果ほど、結論として信頼してはならない。

grep の空結果は、

  • 本当に存在しない
  • CRLF / エンコーディングで取りこぼした
  • 検索パターンが微妙にずれていた

——のどれなのか、出力だけでは区別できません。エラーで落ちてくれれば気づけますが、静かに空を返すのがいちばん危険です。だから「無い」を確定させたいときほど、別系統のコマンド(VCS 専用)で裏を取ります。

運用Tips:判定をAIに任せるときの3つの約束

AI に「ファイルにこのエントリがあるか確認して」と頼むときの、実務的な約束事です。

  • 無視 / 追跡 / コミット内容の判定は、grep ではなく git check-ignore / git ls-files / git show HEAD: を使え、とプロンプトに明示する。
  • 空結果=存在しない」と即断せず、異なる出力形式や別コマンドで裏取りしてから結論を出せ、と指示する。今回の -v-n の食い違いがまさにこれです。
  • 結果が矛盾したら、矛盾を放置せず原因まで掘る。grep と check-ignore がずれた時点で「どちらかが嘘をついている」と疑い、CRLF までたどり着いたのが正解でした。

この約束事の価値は、検証時間の削減量で測れる以前に、「ツールが黙って空を返した結果を、AI がそのまま結論にする」という事故を防げる点にあります。

まとめ:黙って敗北するツールを信じない

CRLF や空白、エンコーディングの差は、テキストツールをエラーなしで敗北させます。grep はヒット0を返し、AI も人もそれを「存在しない」と読む。けれど実際には、ツールが取りこぼしているだけかもしれません。

だから、

  1. 無視 / 追跡 / コミット内容の判定は、grep ではなく git check-ignore / git ls-files / git show HEAD: を SoT にする
  2. 同じコマンドでも出力形式(末尾スラッシュ / -n / --no-index)で結果が変わる罠を疑う
  3. 結論は複数の検証方法で裏取りする
  4. ツールが空を返した=存在しない」と即断しない

ツールが落ちて教えてくれるなら、まだ親切です。本当に怖いのは、何事もなかったかのように空を返すツールです。判定をそのツールに委ねるのではなく、状態を正確に知っている VCS に直接問い合わせる——それが、静かな誤判断を防ぐ習慣になります。

Article title:CRLFな設定ファイルはgrepを黙って敗北させる:判定はVCS専用コマンドをSoTにする
Article author:45395
Release time:2026-06-14

記事へのご質問・ご感想をお聞かせください

フィードバックを送る