スクリプトを書くとき、私たちはよく「どう回すか」を考えます。
けれど、もう一つの道があります。
“自分自身を呼び出す”――再帰という設計。
Bashでも、再帰を使うことで処理を階層的に整理し、シンプルに表現することができます。
今回は、スクリプト設計の一つの考え方として「再帰関数」を見ていきましょう。
はじめに:再帰という「考え方」
Bashで処理を設計するとき、多くの人は「ループ(for / while)」で問題を解こうとします。
もちろんそれは王道です。ですが、再帰(recursion)というもう一つの考え方を取り入れると、
「自分自身を呼び出すことで処理を分割・整理する」柔軟な設計が可能になります。
再帰関数とは、関数の中で自分自身を呼び出す関数のこと。
「1つの処理を小さく分けて、再び同じ仕組みで解く」ことができます。
再帰を理解する一番シンプルな例:階乗
#!/bin/bash
factorial() {
local n=$1
if (( n <= 1 )); then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
# 使用例
result=$(factorial 5)
echo "5! = $result"
実行結果
5! = 120
ここで重要なのは「終了条件」と「再帰呼び出し」の2つ。
| 要素 | 役割 |
|---|---|
if (( n <= 1 )) | 終了条件。再帰が無限に続かないようにする。 |
factorial $((n - 1)) | 自分自身を呼び出す。ここで処理が“折りたたまれる”。 |
階乗のように、「大きな問題を小さな同じ問題に分解できる」構造を持つとき、再帰が自然にハマります。
再帰をbashらしく使う:ディレクトリ探索
再帰の魅力は、「階層構造」に強いことです。
たとえば、ファイルシステムを辿るスクリプト。
#!/bin/bash
list_files() {
local dir=$1
for item in "$dir"/*; do
if [ -d "$item" ]; then
list_files "$item" # 再帰呼び出し
elif [ -f "$item" ]; then
echo "$item"
fi
done
}
# 使用例
list_files "/etc"
階層を潜る処理をループだけで書くと、入れ子が深くなり可読性が落ちます。
再帰を使えば「構造そのもの」をコードで表現できるのです。
設計としての再帰 ―「ループより分かりやすくなる」瞬間
再帰関数を使うと、「構造を写す」ようなコードになります。
ループが「動きの流れ」を書くのに対して、
再帰は「階層の関係」をそのままコードに落とし込む。
- 木構造の探索(ディレクトリ・JSONなど)
- 再分割処理(探索・分岐)
- 依存関係の解決(親 → 子 → 孫)
Bashは本格的な再帰言語ではありませんが、
「スクリプトを設計する考え方の道具」として使う価値があります。
注意点:bashの再帰には限界がある
Bashの関数呼び出しはC言語のような深い再帰を想定していません。
深さが数百を超えると、スタックが溢れてエラーになります。
対策と考え方
- 深さが決まっている処理なら問題なし
- 無限再帰を防ぐために、終了条件は厳格に設計する
- 深すぎる処理は
findやxargsなどのUNIXコマンドで置き換える
再帰は「便利なループ」ではなく「構造を写す設計手法」として扱うと安全です。
まとめ:再帰をbashの設計に取り入れる
| 観点 | 再帰の特徴 |
|---|---|
| 設計思想 | 問題を小さく分割し、自分自身で処理する |
| 強み | 階層構造に強い。構造をそのまま表現できる |
| 注意点 | スタック深さに制限あり。無限ループに注意 |
| 活用例 | ディレクトリ探索・依存解決・木構造処理 |
再帰とは「構造を再現する設計」
Bashでもそれは可能であり、
スクリプトを「動かす」から「設計する」へと進化させる道になる。
