はじめに
諸々の事情があり、bashで動的な多重ループ処理(←ちょっと何言ってるかわからない)を書かねばならないことに。
$ ./xxx.sh 5
なんてやったら、5回の多重ループを実施する。という要件です。
bashでそんなことできんのかよ
と思ったんですがあっさり実現できました。ので共有します。
本編
仕様
指定桁数分、すべての数値の組み合わせを求め、出力します。
具体的には
$ ./xxx.sh 3
とした場合、3桁の000~999、計1000個の数値をコンソール出力します1。
コード
以下参照ください(ご使用は自己責任でお願いします)。
$ cat recursive_loop.sh
# !/bin/bash
SOURCE_CHARS="0123456789"
LENGTH=${#SOURCE_CHARS}
# 結果文字列と最大の深さ(初期値は1)
result_str=""
depth_max=1
# 再帰関数本体
function loop_func () {
# 明示的にローカル変数を使う
local _count=${1}
local _i=0
local _before=""
for ((_i=0; _i<${LENGTH}; _i++))
do
# 終端に1文字を連結
_before=${result_str}
result_str=${result_str}${SOURCE_CHARS:${_i}:1}
# 深さが最大となったら結果出力
# _countは0始まりなので、depth_maxから1を引いている
if [ ${_count} -ge $((${depth_max}-1)) ]; then
echo ${result_str}
# 最大でなければ再帰呼び出し
else
loop_func $((${_count}+1))
fi
# if/elseいずれの場合も文字列は連結前に戻す
result_str=${_before}
done
}
# パラメータで深さを受け取る
if [ ${#} -ge 1 ]; then
if [ ${1} -ge 1 ]; then
depth_max=${1}
fi
fi
# 再帰呼び出し開始
loop_func 0
実行結果
$ ./recursive_loop.sh
0
(略)
9
$ ./recursive_loop.sh 2
00
(略)
99
$ ./recursive_loop.sh 3
000
(略)
999
$ ./recursive_test.sh 3 | wc -l
1000
汎用化
多重化するのがループだとごちゃつくので、汎用的に「多重xxx」とするなら以下。
$ cat recursive_xxx.sh
# !/bin/bash
# 最大の深さ(初期値は1)
depth_max=1
# 再帰関数本体
# xxxに応じた処理は暫定でechoにしてある
function xxx_func () {
# 明示的にローカル変数を使う
local _count=${1}
# 深さが最大となったら終了
# _countは0始まりなので、depth_maxから1を引いている
if [ ${_count} -ge $((${depth_max}-1)) ]; then
echo "[${_count}] FINISH (Depth=${depth_max})"
# 最大でなければ再帰呼び出し
else
echo "[${_count}] --->"
xxx_func $((${_count}+1))
echo "[${_count}] <---"
fi
}
# パラメータで深さを受け取る
if [ ${#} -ge 1 ]; then
if [ ${1} -ge 1 ]; then
depth_max=${1}
fi
fi
# 再帰呼び出し開始
xxx_func 0
user@user-VirtualBox:~/zip$ ./recursive_xxx.sh 3
[0] --->
[1] --->
[2] FINISH (Depth=3)
[1] <---
[0] <---
技術?ポイント
- 関数が書ける
- その関数でローカル変数が使える
言語仕様的にこれらを満たすため、再帰呼び出しも可能なのでした。
アルゴリズム的には、頭が混乱しそうでしたが何とかなった
おわりに
感想
bashもやればできる!笑
確認環境
- Ubuntu 18.04 LTS
-
コメントいただいたように、本仕様であれば
seq
コマンドで簡単に実現できますが、あくまで再帰呼び出しの例ということで ↩