shell中可以自定义函数,然后在脚本中可调用,但必须保证先定义再使用。
function_name () {
....
[return int]
}
# 或者
function function_name() {
....
}
重点:
function
关键字可以省略,但加上更容易让人理解这是一个函数定义。return
返回,也可以省略,如果省略将以最后一条命令运行结果作为返回值。return
只能返回数字0~255。function_name parm1 parm2
调用函数仅使用其函数名即可,后面跟函数的参数,使用方式见后面示例。
在函数内部,使用 $n
的方式获取输入参数,数字n表示第几个参数。如 $1
表示第一个参数,$2
表示第二个参数,但如果要获取第十个参数,不能使用 $10
而是要使用 ${10}
。
另外还有几个特殊符号来处理参数:
字符 | 说明 |
---|---|
$n | 获取第n个参数,这里n不是字母n,而是指某个数字,如 $1 、${12} |
$# | 参数个数 |
$* | 所有函数的参数 |
$@ | 与$*相同,区别是加引号时返回参数的形式不一样 |
$? | 获取函数返回值 |
$* 与 $@ 区别:
使用 $?
获取返回值,值得注意的是,函数的返回值只能是0~255。
后面会介绍一些方法去获取一些非数字的返回值的情况。
#!/bin/bash
# 定义一个加法函数,注意这里返回的值必须在0~255之间,否则会报错
function add() {
return $(($1 + $2))
}
# 直接函数名加参数列表调用即可
add 1 2
# 使用$?获取函数输出,这里会打印出 3
echo $?
前面我们得知函数的返回值只能是0~255的数字,但需要返回超过这个范围的数字,或者需要返回字符串、数组、map等类型的数据时,该怎么做呢?
另外我们也知道了可以使用 $n
可以获取第n个参数,但如果想传入数组、map这样的特殊参数,又该如何做呢?
这些问题可以通过如下内容一步步掌握。
默认情况下,所有变量都是全局的,即函数中定义的变量也可以在函数外使用,函数中也可以直接给函数外的变量赋值。所以我们可以使用全局变量来绕开返回值限制的问题。
如果我们希望某个变量仅仅只是函数内局部可见,避免函数外同名变量的相互影响,可以使用 local
关键字定义变量。
示例:
#!/bin/bash
#在函数中直接使用第一个参数给变量赋值
function foo() {
global_var=$1 #定义一个全局变量,并赋值
local local_var=$1 #定义一个局部变量,并赋值
}
echo "before function, global_var: $global_var"
echo "before function: local_var: $local_var"
foo "apple" #调用函数
echo "after function: global_var: $global_var"
echo "after function: local_var: $local_var"
执行结果为:
before function, global_var:
before function: local_var:
after function: global_var: apple
after function: local_var:
可以看到,全局变量在函数外可以成功赋值和使用,而局部变量则不行。
不过,有时候我们为了一些独立性、封装性、解耦合等要求,并不希望都使用全局变量,该如何做可以继续阅读。
shell中使用反引号包含的字符串,会被当做一条命令来执行,返回值是命令的执行的结果,如:
echo `date`
# 打印结果为:2022年 05月 06日 星期五 15:14:26 CST
所以我们可以在函数中调用echo来返回我们想要返回的任一结果,然后通过反引号获取echo的输出来实现获取特殊的返回结果,如:
#!/bin/bash
#通过echo返回一个字符串
function foo() {
echo "this is function echo with parameter: $1"
}
#通过echo返回一个超过255的数字
function bar() {
echo 1024
}
#使用var1和var2接收函数的返回
var1=`foo "apple"`
var2=`bar`
#打印出var1和var2
echo -e "$var1\n$var2"
执行结果:
this is function echo with parameter: apple
1024
关于shell中数组的相关知识,本文暂不介绍,这里假设读者已经掌握了。
函数参数本身其实是识别不了数组,但我们可以把所有参数构建成一个新的数组来使用,再利用前面两点的知识,可以如下使用:
#!/bin/bash
function double_array() {
local array=(`echo "$@"`) #获取输出参数并转化为一个数组类型变量
local size="$#" #获取数组长度,也可以通过${#array[@]}得到
local i
for ((i = 0; i < size; i++)) {
array[$i]=$[ $[array[$i]] * 2 ] #遍历并乘2
}
echo "${array[*]}" #通过echo输出,注意函数内不要加多余的echo语句
}
a=(1 2 3 4 5) #定义一个数组变量
b=(`double_array ${a[*]}`) #将a传入函数,并得到数组b
echo "a: ${a[*]}, size: ${#a[@]}"
echo "b: ${b[*]}, size: ${#b[@]}"
执行结果:
a: 1 2 3 4 5, size: 5
b: 2 4 6 8 10, size: 5
bash 4.1.2版本后加入了map类型,使用 declare -A
定义。如:
declare -A m=(["a"]="apple" ["b"]="boy" ["c"]="cat" ["d"]="dog")
echo "size: ${#m[@]}" #输出map长度
echo "keys: ${!m[@]}" #输出map所有key
echo "values: ${m[@]}" #输出map所有value
echo "m[c]: ${m["c"]}" #输出map中key1对应的值
执行结果:
size: 4
keys: a b c d
values: apple boy cat dog
m[c]: cat
map比数组复杂挺多的,没法一次性拿到map所有的字符串信息,只能自己拼接一下了。
#!/bin/bash
function use_map() {
declare -A map=$1
echo "in function ***************"
echo "size: ${#map[@]}" #输出map长度
echo "keys: ${!map[@]}" #输出map所有key
echo "values: ${map[@]}" #输出map所有value
}
# 这里申明的是一个字符串变量,map变量的话需要先手动转换成字符串
map_str="(["a"]="apple" ["b"]="boy" ["c"]="cat" ["d"]="dog")"
# 注意需要加双引号,当作一个参数传递进去
use_map "$map_str"
# 函数内的map是局部的,这里打印不出map的信息
echo -e "\nout function ***************"
echo "size: ${#map[@]}" #输出map长度
echo "keys: ${!map[@]}" #输出map所有key
echo "values: ${map[@]}" #输出map所有value
执行结果:
in function ***************
size: 4
keys: a b c d
values: apple boy cat dog
out function ***************
size: 0
keys:
values:
要输出一个map,在函数中拼接一个map格式的字符串返回即可。
#!/bin/bash
function get_map() {
local result=""
local keys=("a" "b" "c" "d")
local values=("apple" "boy" "cat" "dog")
local size=${#keys[@]}
for ((i = 0; i < size; i++)) {
result="$result [${keys[$i]}]=${values[$i]}"
}
echo "($result)"
}
declare -A map=`get_map`
echo "size: ${#map[@]}" #输出map长度
echo "keys: ${!map[@]}" #输出map所有key
echo "values: ${map[@]}" #输出map所有value
执行结果:
size: 4
keys: a b c d
values: apple boy cat dog
map整体使用起来比数组麻烦了挺多的,也可以考虑直接定义一个全局变量的map使用。