函数参数

Shell 函数没有形参列表,参数是在调用时传递的。函数内部通过位置参数来访问这些参数。

位置参数

函数内部用 $1$2$3 等来获取第1个、第2个、第3个参数:

greet() {
    echo "你好,$1!"
    echo "你今年 $2 岁"
    echo "来自 $3"
}

greet "小明" 25 "北京"

运行结果:

你好,小明!
你今年 25 岁
来自 北京

参数之间用空格分隔。如果参数本身包含空格,要用引号包起来。

特殊参数变量

除了 $1$2 这些,还有一些特殊变量:

变量含义
$#参数个数
$@所有参数,每个参数用引号包住
$*所有参数,合并成一个字符串
$?上一个命令的退出状态
$$当前进程 PID
show_params() {
    echo "参数个数:$#"
    echo "第一个参数:$1"
    echo "所有参数:$@"
    echo "参数列表:"
    for param in "$@"; do
        echo "  - $param"
    done
}

show_params apple banana cherry

运行结果:

参数个数:3
第一个参数:apple
所有参数:apple banana cherry
参数列表:
  - apple
  - banana
  - cherry

@@ 和 * 的区别

这两个变量看起来一样,但在引号中表现不同:

test_args() {
    echo "使用 \"\$@\":"
    for arg in "$@"; do
        echo "  [$arg]"
    done
    
    echo "使用 \"\$*\":"
    for arg in "$*"; do
        echo "  [$arg]"
    done
}

test_args "hello world" "foo bar"

运行结果:

使用 "$@":
  [hello world]
  [foo bar]
使用 "$*":
  [hello world foo bar]

"$@" 会保留每个参数的边界,"$*" 会把所有参数合并成一个字符串。绝大多数情况应该用 "$@"

参数默认值

可以给参数设置默认值,调用时不传就用默认的:

greet() {
    local name=${1:-"陌生人"}
    local age=${2:-18}
    echo "你好,$name,你今年 $age 岁"
}

greet
greet "小红"
greet "小红" 20

运行结果:

你好,陌生人,你今年 18 岁
你好,小红,你今年 18 岁
你好,小红,你今年 20 岁

${变量:-默认值} 的意思是:如果变量不存在或为空,就用默认值。还有几种类似的写法:

  • ${变量:=默认值}:不存在时赋值并使用默认值
  • ${变量:+替代值}:变量存在时用替代值
  • ${变量:?错误信息}:不存在时报错退出

超过9个参数

$1$9 可以直接写,第10个及以后的参数要用 ${} 包住:

show_tenth() {
    echo "第10个参数是:${10}"
    echo "第11个参数是:${11}"
}

参数移位

shift 命令可以把参数向左移一位,$2 变成 $1$3 变成 $2,以此类推:

process_args() {
    while [ $# -gt 0 ]; do
        echo "处理参数:$1"
        shift
    done
}

process_args a b c d

运行结果:

处理参数:a
处理参数:b
处理参数:c
处理参数:d

shift 在处理命令行选项时特别有用,后面实战章节会详细讲。

小结

  • 函数参数用 $1$2 等访问,$# 是参数个数
  • "$@" 保留参数边界,推荐使用
  • ${变量:-默认值} 可以设置参数默认值
  • 第10个及以后的参数要写成 ${10} 的形式
  • shift 命令可以移动参数位置