本番環境の障害対応で、数ギガバイトのログファイルを cat file.log | grep error のように調べていて、ターミナルが固まった経験はないでしょうか。障害調査・SRE(Site Reliability Engineering、システムの信頼性を運用面から担保する職務)に携わる方や、監視ダッシュボードよりも先にサーバーへ SSH してログを直接見る運用をしている方に向けて、この落とし穴を整理します。
よく使われている cat(catenate、連結の略)というコマンドは、実は「ファイルを1つ表示する」ためのツールではありません。1971年に Ken Thompson と Dennis Ritchie が Version 1 Unix 向けに書いた当初の目的は、複数のファイルを1つのストリームに連結することでした。
何が起きるか:大規模ログでの cat 多用が招く影響
cat で1ファイルだけを引数に渡すと、標準出力(stdout、コマンドの出力先)がターミナル画面に向いているため、内容がそのまま画面に流れます。これは便利な副作用であって、本来の設計目的ではありません。
数ギガバイトのログファイルに対してこれを行うと、ターミナルは全内容を一度にレンダリングしようとして応答が遅くなります。CPU サイクルとカーネルのオーバーヘッド(システムコールやメモリコピーにかかる余分な処理)も無駄に消費されます。
さらに cat file.txt | grep error のようにパイプでつなぐ書き方も要注意です。cat が全内容を読み切ってから grep(テキスト検索コマンド)に渡す構造のため、ファイルサイズが大きいほど無駄な中間処理が増えます。SRE の現場では、障害対応中にターミナルの反応が悪化することが、原因調査そのものを遅らせるリスクになります。
なぜ起きるか:段階的に見る原因の分解
原因は3つの層に分けて理解すると整理しやすいです。
1つ目は歴史的経緯です。Unix の標準出力がデフォルトでターミナルを指すという設計上、cat にファイルを1つ渡すだけで画面表示ができてしまいました。この「たまたま動く」使い方がチュートリアルや書籍を通じて数十年にわたって伝承され、標準的な使い方として定着しました。
2つ目はコマンドの実行モデルです。cat は入力を頭から末尾まで逐次的に読み、出力ストリームへ書き出すだけの単純な処理です。ファイルの構造や意味を理解せず、バイト列をそのまま右から左へ流します。ログのフォーマットに関する知識(JSON か、タイムスタンプ形式か等)は一切考慮されません。
3つ目はパイプライン全体でのコストです。cat file | grep pattern という書き方では、シェルが cat プロセスと grep プロセスを両方起動し、間でパイプバッファ経由のデータ転送が発生します。grep pattern file のように直接ファイルを渡せば、この余分なプロセス起動とデータコピーを省けます。Unix 業界ではこの cat を挟む無駄な書き方を「Useless Use of Cat(UUOC)」と呼び、古くから指摘されているアンチパターンです。
自分の環境が該当するか確認する方法
以下の観点で、自分やチームの運用が該当していないかを確認できます。
- シェル履歴を確認する:
history | grep "cat " | grep -E "grep|awk|sed" | wc -lで、catをパイプの先頭に使っている回数を数える - ログ収集・調査用のスクリプトやランブック(障害対応手順書)内で
catが単独ファイル表示に使われていないか grep する:grep -rn "cat .*\.log" ./scripts ./runbooks - 対象ファイルのサイズを確認する:
ls -lh /var/log/app/app.logやdu -shで数百MB〜数GB級かどうかを見る - Terraform や Ansible などの IaC(Infrastructure as Code)テンプレート内に、デバッグ目的で
catを使ったログ出力タスクが残っていないか確認する
個人の対話的な調査だけなら影響は軽微ですが、CI/CD のログ確認ジョブや、監視エージェントが定期実行するヘルスチェックスクリプトに cat が組み込まれている場合、実行コストが積み重なります。定期実行される処理ほど優先して見直す価値があります。
対策の手順
単純な表示コマンドの置き換えだけでも十分に効果があります。
# NG: 巨大ログをそのまま流す
cat app.log | grep "ERROR"
# OK: grep に直接ファイルを渡す(UUOC を避ける)
grep "ERROR" app.log
# OK: ページャーで必要な範囲だけ読む(less は全体を先読みしない)
less app.log
# OK: 末尾だけを継続監視する(障害調査で最も使う場面)
tail -f app.log
# OK: 特定行数だけ確認する
head -n 100 app.log継続的な監視やオブザーバビリティ(システムの内部状態を外部から把握できる度合い)の観点では、そもそも cat で生ログを読む頻度自体を減らす設計が有効です。構造化ログ(JSON形式など)を Fluent Bit や Vector で集約し、Loki や Elasticsearch のようなログ基盤に送り込んでおけば、grep や cat によるサーバー上での直接調査は最終手段に留められます。SLO(Service Level Objective、サービスレベル目標)違反のアラートからログ基盤へ直接ドリルダウンできる経路を整えておくと、SSH してログファイルを手動で開く場面自体を減らせます。
コマンドラインでの調査効率を高めたい場合は、bat(シンタックスハイライトと git 差分表示付きの cat 代替)や ripgrep(rg、grep より高速な検索ツール)も選択肢になります。ただし本番の踏み台サーバーやコンテナ内には標準ツール以外がインストールされていないことも多いため、grep・less・tail といった POSIX 標準コマンドの使い分けを先に押さえておくのが現実的です。
cat は「ファイルを連結する」コマンドであり、「大きいファイルを1つ読む」用途には less や tail -f、検索には grep を直接使う方が、パイプ越しの無駄なプロセス生成とターミナル負荷を避けられます。まとめ
cat の誤用は個々の実行では小さな無駄ですが、障害対応の緊迫した場面やCI/CDの定期実行スクリプトに組み込まれると、応答遅延やリソース消費として積み重なります。
次のアクションとして、まずは自分のシェル履歴とランブックを grep で確認し、cat file | grep の形が残っていないか棚卸ししてみてください。
見つかった箇所は grep pattern file や less・tail -f に置き換えるだけで、障害対応中のターミナル操作が軽くなります。あわせて、ログ基盤へのドリルダウン経路を見直しておくと、サーバー上で生ログを直接読む場面そのものを減らせます。