函数:定义、参数、返回值 (Functions)

章节概述

本章讲解 Bash 函数的定义与调用、参数传递机制、返回值处理、局部变量作用域、递归函数,以及与 C 函数的对比。

核心理念:Bash 函数没有参数类型和返回类型——一切都是字符串和退出码。真正的”返回值”通过 echo 输出,退出码只表示成功/失败。


第1节:函数定义的两种语法

# 语法1(推荐)
function greet {
    echo "Hello, $1!"
}
 
# 语法2(POSIX 兼容)
greet() {
    echo "Hello, $1!"
}
 
# 调用
greet "World"    # 输出: Hello, World!

两种语法对比:

语法POSIX 兼容带 function 关键字
func() { }
function func { }

建议:优先使用 func() { } 写法,兼容性更好。


第2节:参数传递 9

show_args() {
    echo "脚本名: $0"
    echo "参数1: $1"
    echo "参数2: $2"
    echo "参数3: $3"
    echo "参数个数: $#"
}
 
show_args "hello" 42 "world"

参数相关变量:

变量含义
$1~$9第1到第9个参数
${10}第10个及以后(需花括号)
$#参数个数
$@所有参数(分别引用)
$*所有参数(作为整体)
$0脚本/函数名
# $@ vs $* 的区别
demo() {
    echo "=== \$@ ==="
    for arg in "$@"; do echo "  [$arg]"; done
    echo "=== \$* ==="
    for arg in "$*"; do echo "  [$arg]"; done
}
 
demo "hello world" "foo bar"
# $@: 分别引用 → ["hello world"] ["foo bar"]
# $*: 作为整体 → ["hello world foo bar"]

第3节:return vs echo

# return 只能返回退出码(0-255)
is_root() {
    [ "$(id -u)" -eq 0 ] && return 0 || return 1
}
is_root && echo "是 root" || echo "非 root"
 
# 真正的"返回值"通过 echo
add() {
    echo $(( $1 + $2 ))
}
result=$(add 3 5)
echo "$result"    # 8

return vs echo 对比:

方式返回类型范围用途
return N退出码0-255状态标志(成功/失败)
echo字符串任意实际数据

最佳实践:用 return 表示成功/失败,用 echo 返回数据,通过 $() 捕获。


第4节:局部变量 local

counter=0
 
increment() {
    local counter=0       # 局部变量,不影响全局
    ((counter++))
    echo "函数内: $counter"
}
 
increment    # 函数内: 1
echo "函数外: $counter"   # 0(未受影响)

不使用 local 的陷阱:

x=10
modify() {
    x=999    # 修改了全局变量!
}
modify
echo $x      # 999
 
# ✓ 用 local 避免污染
x=10
safe_modify() {
    local x=999
    echo "函数内: $x"
}
safe_modify   # 函数内: 999
echo $x       # 10(全局未受影响)

规则:函数内始终用 local 声明所有局部变量。


第5节:递归函数

# 阶乘
factorial() {
    local n=$1
    if [ $n -le 1 ]; then
        echo 1
    else
        local prev=$(factorial $((n - 1)))
        echo $((n * prev))
    fi
}
 
echo "5! = $(factorial 5)"    # 120

C 对比:

int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

注意:Bash 递归深度有限(默认约 1000),深层递归应考虑循环替代。


第6节:函数作为参数

# 高阶函数:将函数名作为参数
apply() {
    local func=$1
    shift
    "$func" "$@"
}
 
greet() { echo "Hello, $1!"; }
farewell() { echo "Goodbye, $1!"; }
 
apply greet "Alice"       # Hello, Alice!
apply farewell "Bob"      # Goodbye, Bob!

第7节:函数库与 source

# utils.sh - 公共函数库
log_info()  { echo "[INFO]  $*"; }
log_error() { echo "[ERROR] $*" >&2; }
die()       { log_error "$@"; exit 1; }
 
# main.sh - 主脚本
source ./utils.sh
 
log_info "脚本开始"
[ -f "$1" ] || die "文件不存在: $1"
log_info "处理完成"

source vs 执行:

方式作用域用途
source file.sh当前 shell加载函数、变量
bash file.sh子 shell独立执行

第8节:课后练习

  1. 编写函数 max,接收两个参数返回较大值
  2. 用递归实现斐波那契数列函数
  3. 创建函数库并用 source 加载

相关章节: