Shell函数编程:参数传递与返回值处理详解
1. Shell函数基础与传参机制在Shell脚本开发中函数是实现代码复用的核心组件。与大多数编程语言不同Shell函数的参数传递和返回值处理有其独特的设计哲学。我们先从一个基础示例开始#!/bin/bash # 定义计算器函数 calculate() { local operand1$1 # 第一个参数赋值给局部变量 local operand2$2 # 第二个参数赋值给局部变量 local operator$3 # 第三个参数为操作符 case $operator in ) echo $((operand1 operand2));; -) echo $((operand1 - operand2));; *) echo $((operand1 * operand2));; /) echo $((operand1 / operand2));; *) echo Unsupported operator; return 1;; esac } # 调用示例 result$(calculate 10 5 ) echo 10 5 $result关键细节Shell函数参数通过位置传递$1-$9代表前9个参数$0始终是脚本名称。使用local关键字声明局部变量可避免污染全局命名空间。2. 返回值处理的双轨制Shell函数有两种返回值机制分别适用于不同场景2.1 状态返回值returncheck_service() { pgrep -x $1 /dev/null 21 return $? # 返回上条命令的退出状态 } check_service nginx case $? in 0) echo Service is running;; 1) echo Service not found;; *) echo Unexpected error;; esac状态返回值特点只能是0-255的整数0表示成功非0表示错误通过$?获取返回值适合用于条件判断2.2 数据返回值echoget_timestamp() { date %Y-%m-%d %H:%M:%S } current_time$(get_timestamp) echo Current time: $current_time数据返回值特点可以输出任意字符串通过命令替换$()捕获适合返回计算结果可以返回多行数据使用数组3. 局部变量实战技巧3.1 作用域控制global_varinitial modify_vars() { local local_varprivate global_varmodified # 直接修改全局变量 echo Inside function: echo global_var$global_var echo local_var$local_var } modify_vars echo Outside function: echo global_var$global_var echo local_var$local_var # 输出为空3.2 数组参数传递process_array() { local arr(${!1}) # 间接引用数组参数 for item in ${arr[]}; do echo Processing: $item done } my_array(apple banana orange) process_array my_array[]4. 高级参数处理技术4.1 参数个数检查validate_args() { if [ $# -lt 2 ]; then echo Error: At least 2 arguments required 2 return 1 fi # 正常处理逻辑... }4.2 默认参数设置connect_db() { local host${1:-localhost} local port${2:-5432} local user${3:-postgres} echo Connecting to $user$host:$port }4.3 参数移位处理parse_options() { while [ $# -gt 0 ]; do case $1 in -v|--verbose) verbose1; shift;; -f|--file) file$2; shift 2;; *) args($1); shift;; esac done }5. 错误处理最佳实践5.1 错误传播step1() { return 1; } step2() { return 0; } pipeline() { step1 || return $? step2 || return $? return 0 } pipeline if [ $? -ne 0 ]; then echo Pipeline failed 2 exit 1 fi5.2 错误收集collect_errors() { errors() command1 || errors(command1 failed) command2 || errors(command2 failed) if [ ${#errors[]} -gt 0 ]; then printf %s\n ${errors[]} 2 return 1 fi return 0 }6. 性能优化技巧6.1 减少子shell开销# 低效做法创建子shell sum$(($1 $2)) # 高效做法避免子shell let sum$1$26.2 函数预加载# 在脚本开始时定义所有函数 declare -fx func1 func2 func3 # 后续代码可以多次调用预加载函数7. 调试与日志记录7.1 函数调用跟踪set -x # 开启调试模式 my_function arg1 arg2 set x # 关闭调试模式7.2 带时间戳的日志log() { local timestamp$(date %Y-%m-%d %H:%M:%S) echo [$timestamp] $ script.log } log Function started with parameters: $8. 实战案例配置文件解析器parse_config() { local config_file$1 declare -A config while IFS read -r key value; do # 跳过注释和空行 [[ $key ~ ^# ]] || [[ -z $key ]] continue config[$key]$value done $config_file # 返回关联数组的引用 declare -p config | sed s/^declare -A config// } # 使用示例 eval declare -A my_config$(parse_config app.conf) echo Server port: ${my_config[PORT]}9. 跨版本兼容性处理9.1 数组语法差异# Bash 4.0 支持关联数组 if declare -A test_array /dev/null; then declare -A user_cache else echo WARNING: Associative arrays not supported 2 # 使用替代方案... fi9.2 函数返回值兼容# 兼容不同Shell的返回值处理 get_system_info() { if type -p lsb_release /dev/null; then lsb_release -ds elif [ -f /etc/os-release ]; then source /etc/os-release echo $PRETTY_NAME else uname -srm fi 2/dev/null || echo Unknown }10. 代码组织模式10.1 模块化加载# lib/utils.sh trim_string() { local str$1 echo $str | sed -e s/^[[:space:]]*// -e s/[[:space:]]*$// } # main.sh source $(dirname $0)/lib/utils.sh cleaned$(trim_string hello world )10.2 函数库管理# 自动加载函数库 load_libs() { local lib_dir$(dirname $0)/lib for lib in $lib_dir/*.sh; do source $lib || { echo Failed to load $lib 2 return 1 } done }11. 安全注意事项11.1 参数消毒safe_eval() { local param$1 # 移除所有非字母数字字符 local sanitized${param//[^[:alnum:]]/} eval $sanitized }11.2 权限检查check_privileges() { [[ $EUID -eq 0 ]] return 0 # 检查sudo权限 if ! sudo -n true 2/dev/null; then echo Error: Root privileges required 2 return 1 fi return 0 }12. 性能基准测试benchmark() { local iterations${1:-1000} local start end start$(date %s.%N) for ((i0; iiterations; i)); do $2 /dev/null done end$(date %s.%N) awk -v s$start -v e$end -v n$iterations \ BEGIN {printf %.2f ops/sec\n, n/(e-s)} } # 测试函数调用开销 benchmark 1000 my_function arg1 arg213. 函数文档规范#!/bin/bash # Function: 计算两个数的和 # Usage: sum num1 num2 # Return: 输出两数之和 # Example: sum 10 20 30 sum() { echo $(($1 $2)) } # 生成文档 generate_docs() { grep -E ^# (Function|Usage|Return|Example) $0 | \ sed -e s/^# // -e s/// }14. 单元测试框架#!/bin/bash # test_functions.sh source $(dirname $0)/functions.sh test_addition() { local result$(sum 2 3) [ $result -eq 5 ] || { echo Test failed: 2 3 should be 5, got $result 2 return 1 } return 0 } # 运行所有测试 run_tests() { local failures0 for test_func in $(declare -F | awk /test_/ {print $3}); do if $test_func; then echo PASS: $test_func else echo FAIL: $test_func ((failures)) fi done return $failures }15. 函数式编程技巧15.1 高阶函数map() { local func$1 shift for item in $; do $func $item done } to_upper() { echo $1 | tr [:lower:] [:upper:] } map to_upper hello world15.2 闭包模拟counter() { local count0 increment() { ((count)) echo $count } decrement() { ((count--)) echo $count } case $1 in inc) echo increment;; dec) echo decrement;; esac } # 使用示例 my_counter$(counter inc) $my_counter # 1 $my_counter # 216. 进程间通信16.1 通过文件描述符exec 3 /tmp/pipefile # 创建文件描述符3 writer() { for i in {1..5}; do echo Message $i 3 sleep 1 done } reader() { while read -u 3 line; do echo Received: $line done } # 在不同终端运行 # terminal1: writer # terminal2: reader16.2 使用命名管道mkfifo /tmp/mypipe processor() { while read input; do echo Processing: $input output.log done /tmp/mypipe } # 启动处理器后台运行 processor # 发送数据 echo data1 /tmp/mypipe echo data2 /tmp/mypipe17. 信号处理trap_handler() { echo Caught signal $1, cleaning up... # 清理临时文件等资源 exit 1 } setup_traps() { trap trap_handler SIGINT SIGINT trap trap_handler SIGTERM SIGTERM } long_running_task() { while true; do echo Working... sleep 1 done } setup_traps long_running_task18. 并发控制18.1 使用coprocasync_task() { sleep 3 echo Task completed } coproc ASYNC { async_task; } # 主线程继续执行 echo Main thread working... # 等待后台任务完成 wait $ASYNC_PID read -u ${ASYNC[0]} result echo Async result: $result18.2 并行处理parallel_process() { local max_jobs$1 local job_count0 for item in ${:2}; do ( # 实际处理逻辑 process_item $item ) ((job_count)) if ((job_count % max_jobs 0)); then wait fi done wait }19. 动态代码生成generate_case_statement() { local var$1 shift local cases($) echo case $var in for pattern in ${cases[]}; do echo $pattern) echo \Matched $pattern\;; done echo *) echo \Default case\;; echo esac } # 动态生成并执行代码 patterns(apple banana cherry) eval $(generate_case_statement fruit ${patterns[]})20. 跨语言集成20.1 调用Pythonpython_math() { local expr$1 python3 -c print($expr) } result$(python_math 2**10) echo 2^10 $result20.2 调用AWKawk_stats() { local file$1 awk BEGIN {sum0; count0} {sum$1; count} END {print sum/count, sum, count} $file } read avg total count $(awk_stats data.txt)21. 函数性能分析profile_function() { local func$1 shift echo Profiling $func... times() for i in {1..10}; do start$(date %s.%N) $func $ end$(date %s.%N) times($(awk -v s$start -v e$end BEGIN{print e-s})) done # 计算统计信息 min$(printf %s\n ${times[]} | sort -n | head -1) max$(printf %s\n ${times[]} | sort -n | tail -1) avg$(awk {s$1} END{print s/NR} ${times[*]}) echo Min: $min, Max: $max, Avg: $avg }22. 函数热加载reload_function() { local func_name$1 local func_def$(declare -f $func_name) # 从当前脚本重新加载函数 source (echo $func_def) echo Function $func_name reloaded } # 修改函数定义后可以动态更新 edit_function() { vi /^$1 /path/to/script.sh reload_function $1 }23. 函数版本控制function_version() { local func_name$1 local version${2:-1.0} eval ${func_name}_v${version}() { $(declare -f $func_name | sed 1,2d;$d) } } # 原始函数 greet() { echo Hello, $1; } # 创建版本化函数 function_version greet 1.0 function_version greet 2.0 EOF echo Greetings, $1! EOF # 调用不同版本 greet_v1.0 Alice greet_v2.0 Bob24. 函数元编程create_function() { local name$1 local body$2 eval $name() { $body } } # 动态创建函数 create_function say_hello echo Hello from dynamic function! say_hello25. 函数调用链追踪set -T # 启用函数追踪 trap echo Call stack: ${FUNCNAME[*]} DEBUG deep_call() { local level$1 [ $level -le 0 ] return deep_call $((level-1)) } deep_call 3 set T # 关闭函数追踪26. 函数参数验证validate_params() { local params($) local types($1) shift for i in ${!params[]}; do case ${types[i]} in int) [[ ${params[i]} ~ ^[0-9]$ ]] || return 1;; str) [ -n ${params[i]} ] || return 1;; file) [ -f ${params[i]} ] || return 1;; *) return 1;; esac done return 0 } safe_function() { validate_params int str file $ || { echo Invalid parameters 2 return 1 } # 安全处理逻辑... }27. 函数结果缓存declare -A CACHE memoize() { local func$1 local key$(printf %s|%s $func $*) [ -n ${CACHE[$key]} ] { echo ${CACHE[$key]} return } local result result$($func $) CACHE[$key]$result echo $result } expensive_calc() { sleep 2 echo $(( $1 * $2 )) } # 第一次调用耗时 time memoize expensive_calc 5 7 # 第二次调用立即返回 time memoize expensive_calc 5 728. 函数依赖注入with_dependencies() { local deps($) for dep in ${deps[]}; do type $dep /dev/null 21 || { echo Missing dependency: $dep 2 return 1 } done shift ${#deps[]} $ } # 使用示例 with_dependencies awk sed grep \ process_data input.txt output.txt29. 函数组合模式compose() { local f$1 shift for g in $; do f$g | $f done eval $f } # 创建处理管道 process_text() { compose \ awk {print \$1} \ tr [:lower:] [:upper:] \ sort | uniq -c } echo -e apple\nbanana\napple\ncherry | process_text30. 函数式测试覆盖率coverage_report() { local script$1 declare -A executed_lines # 使用调试陷阱记录执行行号 trap executed_lines[$LINENO]1 DEBUG # 执行脚本 source $script # 生成报告 local total_lines0 covered_lines0 while IFS read -r line; do ((total_lines)) [ -n ${executed_lines[$total_lines]} ] ((covered_lines)) done $script echo Coverage: $covered_lines/$total_lines ($((covered_lines*100/total_lines))%) }