一、基本使用

shell中可以自定义函数,然后在脚本中可调用,但必须保证先定义再使用。

1. 函数定义

function_name () {
    ....
    [return int]
}
# 或者
function function_name() {
   ....
}

重点:

  • function关键字可以省略,但加上更容易让人理解这是一个函数定义。
  • 可以手动调用return返回,也可以省略,如果省略将以最后一条命令运行结果作为返回值
  • 注意!!return只能返回数字0~255

2. 函数调用

function_name parm1 parm2

调用函数仅使用其函数名即可,后面跟函数的参数,使用方式见后面示例。

3. 获取参数

在函数内部,使用 $n 的方式获取输入参数,数字n表示第几个参数。如 $1 表示第一个参数,$2 表示第二个参数,但如果要获取第十个参数,不能使用 $10 而是要使用 ${10}

另外还有几个特殊符号来处理参数:

字符

说明

$n

获取第n个参数,这里n不是字母n,而是指某个数字,如 $1${12}

$#

参数个数

$*

所有函数的参数

$@

与$*相同,区别是加引号时返回参数的形式不一样

$?

获取函数返回值

$* 与 $@ 区别:

  • 相同点:都是表示所有参数。
  • 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,则 “$*” 表示 “1 2 3”(一个参数),而 “$@” 表示 “1” “2” “3”(三个参数)。

4. 获取返回值

使用 $? 获取返回值,值得注意的是,函数的返回值只能是0~255

后面会介绍一些方法去获取一些非数字的返回值的情况。

5. 示例

#!/bin/bash

# 定义一个加法函数,注意这里返回的值必须在0~255之间,否则会报错
function add() {
    return $(($1 + $2))
}

# 直接函数名加参数列表调用即可
add 1 2

# 使用$?获取函数输出,这里会打印出 3
echo $?

二、进阶使用

前面我们得知函数的返回值只能是0~255的数字,但需要返回超过这个范围的数字,或者需要返回字符串、数组、map等类型的数据时,该怎么做呢?

另外我们也知道了可以使用 $n 可以获取第n个参数,但如果想传入数组、map这样的特殊参数,又该如何做呢?

这些问题可以通过如下内容一步步掌握。

1. 全局变量和局部变量

默认情况下,所有变量都是全局的,即函数中定义的变量也可以在函数外使用,函数中也可以直接给函数外的变量赋值。所以我们可以使用全局变量来绕开返回值限制的问题

如果我们希望某个变量仅仅只是函数内局部可见,避免函数外同名变量的相互影响,可以使用 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:

可以看到,全局变量在函数外可以成功赋值和使用,而局部变量则不行。

不过,有时候我们为了一些独立性、封装性、解耦合等要求,并不希望都使用全局变量,该如何做可以继续阅读。

2. 使用反引号``获取函数输出

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

3. 给函数传递数组,函数返回数组

关于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

4. 给函数传递map,函数返回map

a. 扩展知识:shell中的map使用

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
b. 给函数传递map

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:
c. 函数输出map

要输出一个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使用。