向Bash函数传递参数

向Bash函数传递参数

技术背景

在Bash脚本编程中,函数是组织代码和实现代码复用的重要手段。与其他高级编程语言不同,Bash函数处理参数的方式有其独特之处。理解如何向Bash函数传递参数以及如何在函数内部使用这些参数,对于编写高效、灵活的脚本至关重要。

实现步骤

函数声明

Bash中有两种典型的函数声明方式:

1
2
3
function function_name {
command...
}

或者

1
2
3
function_name () {
command...
}

推荐使用第二种方式。

传递参数调用函数

调用带参数的函数时,使用如下方式:

1
function_name "$arg1" "$arg2"

函数通过参数的位置(而非名称)来引用传递的参数,即$1$2 等,$0 是脚本本身的名称。

示例

1
2
3
function_name () {
echo "Parameter #1 is $1"
}

需要注意的是,函数的调用必须在其声明之后,如下所示:

1
2
3
4
5
6
7
#!/usr/bin/env sh

foo 1 # 这会失败,因为foo还未声明
foo() {
echo "Parameter #1 is $1"
}
foo 2 # 这会成功

核心代码

简单参数传递示例

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
echo "parameterized function example"
function print_param_value(){
value1="${1}" # $1 代表第一个参数
value2="${2}" # $2 代表第二个参数
echo "param 1 is ${value1}" # 作为字符串
echo "param 2 is ${value2}"
sum=$(($value1+$value2)) # 作为数字处理
echo "The sum of two value is ${sum}"
}
print_param_value "6" "4" # 以空格分隔的值
print_param_value "$1" "$2" # 脚本执行时的参数 $1 和 $2

传递数组示例

1
2
3
4
5
6
7
8
9
function callingSomeFunction ()
{
for value in "$@"; do # 使用 "$@" 处理所有参数
:
done
}

someArray=(1 2 3)
callingSomeFunction "${someArray[@]}" # 扩展为所有数组元素

传递值和数组示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function linearSearch ()
{
local myVar="$1"
shift 1 # 从参数列表中移除 $1
for value in "$@"; do # 表示剩余的参数
if [[ $value == $myVar ]]; then
echo -e "Found it!\t... after a while."
return 0
fi
done
return 1
}

someStringValue="a"
someArray=("a" "b" "c")
linearSearch $someStringValue "${someArray[@]}"

按引用传递数组(Bash 4.3及以上)

1
2
3
4
5
6
7
8
9
10
function callingSomeFunction ()
{
local -n someArray=$1 # 也可以使用 ${1:?} 使参数成为必需
for value in "${someArray[@]}"; do
:
done
}

myArray=(1 2 3)
callingSomeFunction myArray # 传递数组名称,而非扩展值

命名参数示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
shopt -s expand_aliases

function assignTrap {
local evalString
local -i paramIndex=${__paramIndex-0}
local initialCommand="${1-}"

if [[ "$initialCommand" != ":" ]]
then
echo "trap - DEBUG; eval \"${__previousTrap}\"; unset __previousTrap; unset __paramIndex;"
return
fi

while [[ "${1-}" == "," || "${1-}" == "${initialCommand}" ]] || [[ "${#@}" -gt 0 && "$paramIndex" -eq 0 ]]
do
shift # 跳过第一个冒号 ":" 或下一个参数的逗号 ","
paramIndex+=1
local -a decorators=()
while [[ "${1-}" == "@"* ]]
do
decorators+=( "$1" )
shift
done

local declaration=
local wrapLeft='" '
local wrapRight='" '
local nextType="$1"
local length=1

case ${nextType} in
string | boolean) declaration="local " ;;
integer) declaration="local -i" ;;
reference) declaration="local -n" ;;
arrayDeclaration) declaration="local -a"; wrapLeft= ; wrapRight= ;;
assocDeclaration) declaration="local -A"; wrapLeft= ; wrapRight= ;;
"string["*"]") declaration="local -a"; length="${nextType//[a-z\[\]]}" ;;
"integer["*"]") declaration="local -ai"; length="${nextType//[a-z\[\]]}" ;;
esac

if [[ "${declaration}" != "" ]]
then
shift
local nextName="$1"

for decorator in "${decorators[@]}"
do
case ${decorator} in
@readonly) declaration+="r" ;;
@required) evalString+="[[ ! -z \$${paramIndex} ]] || echo \"Parameter '$nextName' ($nextType) is marked as required by '${FUNCNAME[1]}' function.\"; " >&2 ;;
@global) declaration+="g" ;;
esac
done

local paramRange="$paramIndex"

if [[ -z "$length" ]]
then
# ...rest
paramRange="{@:$paramIndex}"
# 去除前导的 ...
nextName="${nextName//./}"
if [[ "${#@}" -gt 1 ]]
then
echo "Unexpected arguments after a rest array ($nextName) in '${FUNCNAME[1]}' function." >&2
fi
elif [[ "$length" -gt 1 ]]
then
paramRange="{@:$paramIndex:$length}"
paramIndex+=$((length - 1))
fi

evalString+="${declaration} ${nextName}=${wrapLeft}\$${paramRange}${wrapRight}; "

# 继续处理下一个参数
shift
fi
done
echo "${evalString} local -i __paramIndex=${paramIndex};"
}

alias args='local __previousTrap=$(trap -p DEBUG); trap "eval \"\$(assignTrap \$BASH_COMMAND)\";";'

function example { args : string firstName , string lastName , integer age } {
echo "My name is ${firstName} ${lastName} and I am ${age} years old."
}

最佳实践

  • 参数引用:在函数内部引用参数时,使用双引号将参数括起来,以防止参数值包含空格等特殊字符时出现问题。
  • 参数验证:在函数内部对传递的参数进行验证,确保参数的类型和值符合预期。
  • 代码可读性:使用命名参数或添加注释来提高代码的可读性,特别是在处理多个参数时。

常见问题

函数未声明就调用

在调用函数之前,必须先声明函数,否则会出现“command not found”错误。

参数传递格式错误

在传递参数时,要注意使用正确的格式,避免使用括号调用函数,Bash函数的参数传递方式类似于 shell 命令。

命名参数兼容性问题

命名参数的实现依赖于一些特定的技巧和特性,可能在不同版本的 Bash 中存在兼容性问题。在使用时,需要确保脚本运行的环境支持相应的功能。


向Bash函数传递参数
https://119291.xyz/posts/passing-parameters-to-a-bash-function/
作者
ww
发布于
2025年6月3日
许可协议