一、函数基础
函数是 Shell 脚本中用于代码复用和模块化设计的核心工具,可将重复逻辑封装为独立单元。
1. 定义与调用
# 定义方式1:function 关键字
function say_hello() {
echo "Hello, $1!"
}
# 定义方式2:简化语法
show_time() {
date +"%Y-%m-%d %H:%M:%S"
}
# 调用函数
say_hello "World" # 输出:Hello, World!
current_time=$(show_time)
echo "当前时间: $current_time"
2. 参数传递
- 位置参数:函数内通过
$1
、$2
获取参数 - 特殊变量:
$#
(参数个数)、$@
(所有参数数组)
sum() {
local total=0
for num in "$@"; do
((total += num))
done
echo $total
}
result=$(sum 10 20 30) # 输出:60
二、作用域与变量
1. 局部变量
-
local
关键字:限制变量作用域仅在函数内
test_scope() {
local var1="内部变量"
var2="全局变量"
}
test_scope
echo $var1 # 空值(局部变量不可见)
echo $var2 # 输出:全局变量
2. 返回值机制
- 默认返回值:函数最后一条命令的退出状态码(
$?
) - 显式返回:
return N
(0-255)
check_file() {
[[ -f "$1" ]] && return 0 || return 1
}
check_file "/etc/hosts"
if [ $? -eq 0 ]; then
echo "文件存在"
fi
三、高级用法
1. 返回复杂数据
# 返回字符串
get_config() {
echo "host=127.0.0.1"
echo "port=3306"
}
# 读取多行返回值到数组
mapfile -t config < <(get_config)
echo "Host: ${config[0]#*=}" # 输出:127.0.0.1
2. 递归调用
factorial() {
local n=$1
if ((n <= 1)); then
echo 1
else
echo $((n * $(factorial $((n-1)))))
fi
}
echo "5! = $(factorial 5)" # 输出:120
3. 函数库封装
# 文件:utils.sh
#!/bin/bash
log() {
echo "[$(date)] $1" >> "/var/log/myscript.log"
}
# 主脚本中引用
source utils.sh
log "程序启动"
四、实战场景示例
1. 日志处理模块
# 定义日志级别函数
log_info() { log "INFO" "$1"; }
log_error() { log "ERROR" "$1"; }
log() {
local level="$1"
local message="$2"
printf "[%s] %-5s - %s\n" "$(date +'%Y-%m-%d %T')" "$level" "$message"
}
log_info "用户登录成功"
log_error "数据库连接失败"
2. 批量文件处理
process_images() {
local output_dir="$1"
mkdir -p "$output_dir"
for img in *.jpg; do
convert "$img" -resize 800x600 "$output_dir/$img"
done
}
# 调用
process_images "./thumbnails"
3. 服务状态管理
service_ctl() {
case "$1" in
start) systemctl start $2 ;;
stop) systemctl stop $2 ;;
restart) systemctl restart $2 ;;
*) echo "用法: $0 {start|stop|restart} <服务名>"
esac
}
service_ctl restart nginx
五、最佳实践与调试
1. 错误处理
safe_delete() {
local file="$1"
if [[ ! -e "$file" ]]; then
echo "错误:文件不存在" >&2
return 1
fi
rm -f "$file" || return $?
}
2. 调试技巧
# 显示函数调用栈
debug_func() {
echo "函数名: ${FUNCNAME[0]}"
echo "调用者: ${FUNCNAME[1]}"
}
# 使用 trap 追踪
trap 'echo "正在执行: ${FUNCNAME[0]}"' DEBUG
3. 性能优化
# 避免频繁调用外部命令
fast_sum() {
local sum=0
while read num; do
((sum += num))
done
echo $sum
}
# 使用管道批量处理
seq 100 | fast_sum # 输出:5050
六、注意事项
- 避免命名冲突:函数名不要与内置命令重复
- 参数验证:始终检查输入合法性
- 资源释放:在函数结束时清理临时文件
- 跨脚本调用:通过
source
或.
加载函数库
通过合理使用函数,可以实现:
✅ 代码复用率提升
✅ 脚本可维护性增强
✅ 复杂任务模块化分解
建议结合 help function
和 man bash
深入学习高级特性。