Shell 脚本编程详细指南:第五章 - 函数与参数传递
Shell 脚本编程详细指南:第五章 - 函数与参数传递
引言:函数在脚本工程化中的核心价值
函数是Shell脚本实现模块化编程的基石。本章将深入解析函数编程的各个方面,从基础定义到高级应用,助您构建可维护、可重用的脚本架构。我们将重点探讨参数处理、返回值机制和工程化实践。
1. 函数定义的多范式解析
1.1 定义方式对比
语法形式 | 兼容性 | 特点 |
---|---|---|
func_name() { ... } | POSIX兼容 | 推荐标准写法 |
function func_name | Bash/Korn | 支持额外特性 |
function () { ... } | Zsh扩展 | 匿名函数实现 |
推荐写法:
# 标准POSIX函数
cleanup() {# 资源清理代码
}# Bash增强函数
function log_message {# 日志记录代码
}
1.2 函数生命周期管理
declare -F # 列出所有函数
unset -f function_name # 删除函数
typeset -f function_name # 查看函数定义
2. 参数处理高级技巧
2.1 参数访问方式
变量 | 描述 | 示例 |
---|---|---|
$# | 参数个数 | if [ $# -ne 2 ]; then |
$@ | 全部参数(保持分隔) | for arg in "$@"; do |
$* | 全部参数(合并为字符串) | echo "$*" |
shift | 参数左移 | shift 2 |
2.2 复杂参数处理示例
命名参数处理:
parse_args() {while [[ $# -gt 0 ]]; docase $1 in-u|--user)user="$2"shift 2;;-d|--debug)debug_mode=trueshift;;*)files+=("$1")shift;;esacdone
}
数组参数传递:
process_files() {local -n arr=$1 # 使用namereffor file in "${arr[@]}"; doecho "处理: $file"done
}files=(*.txt)
process_files files
3. 返回值与状态管理
3.1 返回值机制对比
方法 | 数据类型 | 取值范围 | 获取方式 |
---|---|---|---|
return | 整数 | 0-255 | $? |
echo +命令替换 | 任意 | 无限制 | var=$(func) |
全局变量 | 任意 | 无限制 | 直接访问变量 |
文件/管道 | 任意 | 无限制 | 读取文件/管道 |
3.2 复杂数据返回实现
JSON格式返回:
get_system_info() {local cpu=$(grep 'model name' /proc/cpuinfo | head -1 | cut -d':' -f2)local mem=$(free -h | awk '/Mem/{print $2}')echo "{\"cpu\": \"$cpu\", \"memory\": \"$mem\"}"
}# 使用jq解析
info=$(get_system_info)
echo "$info" | jq '.cpu'
多值返回技巧:
calculate() {local sum=$(( $1 + $2 ))local product=$(( $1 * $2 ))echo "$sum $product"
}read sum product <<< $(calculate 3 4)
echo "和: $sum, 积: $product"
4. 函数库与模块化开发
4.1 创建函数库
lib/utils.sh:
#!/bin/bashlog::info() {echo "[$(date '+%F %T')] INFO: $@"
}log::error() {echo "[$(date '+%F %T')] ERROR: $@" >&2
}text::trim() {local str="$*"echo "$str" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}
主脚本调用:
source lib/utils.shlog::info "程序启动"
cleaned=$(text::trim " 示例文本 ")
4.2 函数重载实现
# Bash不支持原生重载,但可通过参数检查模拟
file_exists() {if [ $# -eq 1 ]; then[ -f "$1" ]elif [ $# -eq 2 ]; then[ -f "$1" ] && grep -q "$2" "$1"elsereturn 1fi
}
5. 实战案例解析
案例1:数据库备份函数
#!/bin/bashdb::backup() {local db_name="$1"local backup_dir="${2:-/var/backups}"local timestamp=$(date +%Y%m%d_%H%M%S)local backup_file="${backup_dir}/${db_name}_${timestamp}.sql.gz"if ! mysqldump "$db_name" | gzip > "$backup_file"; thenecho "备份失败: $db_name" >&2return 1fiecho "备份成功: $backup_file"return 0
}# 调用示例
db::backup "webapp_db"
案例2:配置管理函数
#!/bin/bashconfig::get() {local config_file="$1"local key="$2"awk -F= -v k="$key" '$1==k {sub(/^[^=]*= */, ""); print}' "$config_file"
}config::set() {local config_file="$1"local key="$2"local value="$3"if grep -q "^$key=" "$config_file"; thensed -i "s/^$key=.*/$key=$value/" "$config_file"elseecho "$key=$value" >> "$config_file"fi
}
6. 最佳实践与调试技巧
6.1 函数开发规范
-
命名规范:
- 使用小写下划线命名法
- 模块前缀:
module::function
- 示例:
log::error
,db::backup
-
参数验证模板:
validate_arguments() {if [[ $# -lt 2 ]]; thenecho "用法: ${FUNCNAME[1]} 参数1 参数2" >&2return 1fi[[ "$1" =~ ^[0-9]+$ ]] || return 2[ -d "$2" ] || return 3
}
6.2 调试技巧
调试模式启用:
#!/bin/bash
set -euo pipefail # 严格模式
set -x # 调试模式# 函数内局部调试
complex_func() {set -x# 函数代码set +x
}
函数调用追踪:
PS4='+ ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]}() '
set -x
进阶技巧
信号处理函数
trap_handler() {echo "捕获信号:$1"cleanupexit 1
}trap 'trap_handler SIGINT' SIGINT
trap 'trap_handler SIGTERM' SIGTERM
函数性能优化
# 避免在循环中重复定义函数
declare -f func > /dev/null || {func() {# 函数实现}
}
本章总结
本章深入探讨了Shell函数编程的各个方面,重点包括:
- 多种函数定义方式的对比与选择
- 高级参数处理技术
- 复杂返回值机制
- 模块化开发实践
- 实际工程案例解析
进阶练习:
编写一个函数库实现以下功能:
- 彩色日志输出(不同级别不同颜色)
- 进度条显示功能
- 输入验证函数(邮箱、IP、数字范围等)
- 文件下载函数(支持重试机制)