はじめに (対象読者・この記事でわかること)

この記事は、Linux/macOSのターミナルで「ディレクトリ配下のすべてのファイルを一気に表示したい」と思っている方を対象にしています。
特に、サーバー環境でfindxargsが使えない、あるいはコマンドが制限されているケースで困っている方に最適です。

読み進めることで、Bashの組み込み機能(glob/配列/リダイレクト)だけで再帰的にファイルを結合・表示する方法が身につきます。
外部コプロセスを起動しないため、高速かつ依存関係ゼロで動作するスクリプトを書けるようになります。

前提知識

この記文を読み進める上で、以下の知識があるとスムーズです。

  • Bashの基本的な文法(変数、ループ、リダイレクト)
  • ファイルパスのグロブ展開(ワイルドカード)の概念
  • 標準エラー出力(2)と標準出力(1)の違い

なぜfind/xargsを使いたくないのか

findxargsは非常に便利ですが、以下の理由で「使えない/使いたくない」場面があります。

  1. 最小限のコンテナ/組み込み環境
    AlpineやDistroless、BusyBoxではfindutilsxargsが入っていないことがあります。
  2. 大量ファイルでのボトルネック
    findは再起動ごとにstatを大量に呼び出し、ファイル数が多いと明らかに遅くなります。
  3. 特殊文字対策の煩雑さ
    ファイル名にスペースや改行が含まれると、find -print0xargs -0の組み合わせが必須になり、コマンドが長くなりがちです。

Bash 4以降であれば、組み込みの**(グロブ)と配列でこれらの問題を回避できます。

Bash組み込みだけで再帰catする実装

ここでは、外部コマンドを一切使わず「カレント以下の全ファイルを順番にcatする」シェル関数を作ります。
エラーは標準エラーに、ファイル内容は標準出力に分離して、後続のパイプやリダイレクトで加工しやすくしています。

ステップ1: 再帰グロブを有効にする

Bash 4+では**がディレクトリを再帰的にマッチします。まずオプションを有効化します。

Bash
shopt -s globstar # ** を有効化 shopt -s nullglob # マッチしない場合に空文字列になる(エラー回避)

ステップ2: 配列にファイル一覧を展開してcat

ファイル名を配列に格納して順次catします。
これにより、ファイル名にスペースや改行が含まれていても安全です。

Bash
rec_cat() { local files=() file # 隠しファイルも含めて再帰的に取得 for file in **/* .*; do [[ -f $file ]] && files+=("$file") done # ファイルがなければ早期リターン ((${#files[@]})) || return 0 # 全ファイルを順にcat for file in "${files[@]}"; do # エラーはstderrに、中身はstdoutに cat -- "$file" 2>/dev/null || printf 'rec_cat: %s: %s\n' "$file" "$?" >&2 done }

一行で呼び出す場合は以下の通りです。

Bash
shopt -s globstar nullglob; for f in **/* .*; do [[ -f $f ]] && cat -- "$f"; done

ステップ3: 必要に応じてソートやフィルタを追加

ファイル名順に処理したい場合はprintf '%s\n' "${files[@]}" | sort -zしてからループします。
拡張子で絞りたい場合は[[ $file == *.log ]]のように条件を追加するだけです。

Bash
# 例: *.logファイルだけcat for file in **/*.log; do [[ -f $file ]] && cat -- "$file" done

ハマった点と解決策

  • Argument list too long
    デフォルトの配列展開で"$files[@]"を一度にcatに渡すと、ファイル数が多いとエラーになります。
    → 1ファイルずつcatするループにすることで回避できます。

  • 隠しディレクトリの扱い
    **/*は隠しファイルを拾わないため、.*も併せてループします。
    ただし...は除外するため、[[ -f $file ]]でチェックしています。

  • バイナリファイルが混入した場合
    ターミナルが乱れることがあるので、catの代わりにfile -b --mime-type "$file"でテキストかチェックしてからcatする運用も推奨されます。

まとめ

本記事では、外部コマンドなしでディレクトリ配下の全ファイルを再帰的にcatする方法を解説しました。

  • Bash 4以降のglobstarで再帰グロブを有効化
  • 配列にファイルパスを展開して安全にループ
  • 1ファイルずつcatすることで大量ファイルでも高速かつ安全

このテクニックを使えば、最小限の環境でも高速にファイル結合が可能です。
次回は「並列化と進捗表示」を追加して、より大規模なログ解析にも対応できるように改良したいと思います。

参考資料

  • Bash Reference Manual – 4.3.2 The Shopt Builtin
    https://www.gnu.org/software/bash/manual/bash.html#The-Shopt-Builtin
  • Bash Hackers Wiki – glob
    https://wiki.bash-hackers.org/syntax/expansion/glob
  • POSIX Programmer’s Manual – cat
    https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cat.html