5.3.4 递归与函数作用域
4. 递归与函数作用域#
递归用于将复杂问题拆解为更小的同类问题;作用域用于控制变量可见性,避免函数间相互污染。本节以可运行脚本为主,涵盖环境准备、示例、排错与练习。
原理草图:递归调用与作用域#
环境准备与安装#
目标工具
- bash:脚本运行环境
- tree:辅助查看目录结构
- shellcheck:脚本检查(可选)
安装命令(Debian/Ubuntu)
sudo apt-get update
sudo apt-get install -y bash tree shellcheck
安装命令(CentOS/RHEL)
sudo yum install -y bash tree shellcheck
命令解释
- apt-get install -y / yum install -y:自动安装并跳过确认。
- tree:展示目录树,便于验证递归结果。
- shellcheck:静态检查 Shell 语法与最佳实践。
递归示例:遍历目录并统计文件数#
示例脚本:/opt/scripts/rec_count.sh
#!/usr/bin/env bash
set -euo pipefail
count_files() {
local dir="$1"
local cnt=0
local f
# 递归出口:目录不存在
[[ -d "$dir" ]] || { echo 0; return; }
for f in "$dir"/*; do
[[ -e "$f" ]] || continue
if [[ -d "$f" ]]; then
cnt=$((cnt + $(count_files "$f")))
else
cnt=$((cnt + 1))
fi
done
echo "$cnt"
}
target_dir="${1:-/var/log}"
total=$(count_files "$target_dir")
echo "total files in $target_dir: $total"
运行与预期输出
bash /opt/scripts/rec_count.sh /etc
# 预期输出类似:
# total files in /etc: 1234
命令解释
- set -euo pipefail:遇错退出、未定义变量报错、管道错误传播。
- local:定义函数内变量,避免污染全局。
- [[ -d "$dir" ]]:判断目录存在,防止递归错误。
递归示例:计算阶乘(输入校验)#
示例脚本:/opt/scripts/fact.sh
#!/usr/bin/env bash
set -euo pipefail
is_number() {
[[ "$1" =~ ^[0-9]+$ ]]
}
fact() {
local n="$1"
(( n <= 1 )) && { echo 1; return; }
echo $(( n * $(fact $((n-1))) ))
}
n="${1:-5}"
if ! is_number "$n"; then
echo "Error: input must be a non-negative integer" >&2
exit 1
fi
echo "fact($n)=$(fact "$n")"
运行与预期输出
bash /opt/scripts/fact.sh 6
# 预期输出:
# fact(6)=720
函数作用域与变量管理示例#
示例脚本:/opt/scripts/scope.sh
#!/usr/bin/env bash
set -euo pipefail
name="global"
count=0
demo() {
local name="local"
local count=10
echo "inside name=$name, count=$count"
}
demo
echo "outside name=$name, count=$count"
预期输出
inside name=local, count=10
outside name=global, count=0
命令解释
- local name="local":仅在函数内生效。
- 未 local 的变量会影响全局,需统一前缀命名避免冲突。
作用域 + 递归:带深度限制的遍历#
示例脚本:/opt/scripts/walk_depth.sh
#!/usr/bin/env bash
set -euo pipefail
walk() {
local dir="$1"
local depth="$2"
(( depth <= 0 )) && return
local f
for f in "$dir"/*; do
[[ -e "$f" ]] || continue
echo "$f"
[[ -d "$f" ]] && walk "$f" $((depth-1))
done
}
walk "${1:-/var/log}" "${2:-2}"
运行与预期输出
bash /opt/scripts/walk_depth.sh /etc 1
# 仅输出 /etc 目录第一层文件/目录
排错与调试#
常见问题与解决
1. 无限递归
- 原因:缺少终止条件或输入错误。
- 处理:增加深度参数或判断目录存在。
2. “argument list too long”
- 原因:目录中文件过多,"$dir"/* 展开过大。
- 处理:用 find 替代遍历。
3. 变量污染导致结果不一致
- 原因:未使用 local。
- 处理:函数内变量全部使用 local。
调试命令
bash -x /opt/scripts/rec_count.sh /etc # 跟踪执行流程
shellcheck /opt/scripts/rec_count.sh # 静态检查
练习#
-
练习 1:统计目录内“可执行文件”数量
- 要求:递归统计-x文件数。
- 提示:用[[ -x "$f" ]]判断。 -
练习 2:改写阶乘为非递归
- 要求:用for循环计算,避免深层递归。 -
练习 3:记录递归深度
- 要求:每次递归打印“当前深度 + 路径”。
参考命令说明
- [[ -x file ]]:判断文件可执行。
- for i in {1..n}:循环累计。