Shell编程从入门到精通——函数

和其他编程语言一样,Shell语言中也存在函数,通过函数可已将实现某一任务的命令进行封装,可以提高程序的可读性和重用性。

一、函数

1.什么是函数

函数就是将一组功能相对独立的代码集中起来形成一个代码块,这个代码块可以实现某个具体的功能。

2.函数的定义

在Shell中有两种定义函数的方法:

function_name ()
{
    statement1
    statement2
    ......
}

或者

function function_name ()
{
    statement1
    statement2
    ......
}

Shell中函数的命名规则和变量的命名规则基本相同,可以使用任意数字、字母或者下划线,但是只能以字母或者下划线开头。另外用户应该使用具有意义的单词定义函数名,提高代码的可读性。
注意:函数必须在调用之前定义。

#!/usr/bin/env bash

#定义函数
func()
{
    echo "Hello World!"
}
#调用函数
func

Output:

$ sh test.sh
Hello World!

3.函数的调用

函数被定义后,就可以调用函数了,调用函数的语法如下:

function_name param1 param2 ......

function_name为调用的函数名称,后面是传入的参数。

#!/usr/bin/env bash

#定义函数
func()
{
    echo "Hello World! You Input Number is $@"
}
#调用函数
func 1 2 3 4

Output:

$ sh test.sh
Hello World! You Input Number is 1 2 3 4

注意:调用函数的时候不需要圆括号。

4.函数的链接

函数链接是指在一个函数中调用另外一个函数的过程。

#!/usr/bin/env bash

#定义函数
func()
{
    echo "Hello World! You Input Number is $@"
}
main()
{
    func 1 2 3 4
    echo "I am main function"
}
#调用函数
main

Output:

$ sh test.sh
Hello World! You Input Number is 1 2 3 4
I am main function

注意:调用函数的时候必须满足先定义再调用的顺序。

5.函数的返回值

再Shell中,函数的返回值为return语句来返回某个数值,这个数值的取值范围为0~255。这个return与exit命令返回命令执行的状态码类似,可以通过$?获取函数的返回值。

#!/usr/bin/env bash

#定义函数
length()
{
    echo "Hello World"
    return 100
}
length
echo "$?"

Output:

$ sh test.sh
Hello World
100

除了用return返回值外,用户可以将需要返回的数据写入到标准输出,然后调用程序中的函数的执行结果赋值给一个变量,这样也可以实现函数返回值的传递。如下,通过echo传递函数返回值:

#!/usr/bin/env bash

#定义函数
length()
{
    str=$1
    result=0
    if [ "$str" != "" ]; then
        result=${#str}
    fi
    echo "$result"
}

len=$(length "hahaha123")
echo "the strings's length is $len"

Output:

$ sh test.sh
the strings's length is 9

6.函数和别名

在Shell中用户可以使用alias命令来设置命令的别名,语法如下:

alias name="command"

其中name是别名,command是要取别名的命令。
函数和别名的类似之处在于,他们都是通过一个名称的映射到一个或者一组命令,无论是函数还是别名,在调用的时候都是执行一个或者一组相关的命令。

7.再议全局变量和局部变量

除了与函数参数相关联的特殊变量外,其他所有变量都是全局有效的。另外函数内部除了使用local关键字修饰的变量,其他变量也是全局变量。

二、函数的参数

1.含有参数的函数调用方法

这个在前面已经用过了,基本语法如下:

function_name arg1 arg2 ....

注意:函数调用的参数用空格隔开。

2.获取函数参数的个数

用户可以通过使用$#来获取函数的参数个数。

#!/usr/bin/env bash

#定义函数
func()
{
    echo "Hello World! You Input Number Count is $#"
    echo "$@"
}
func 1 2 3 4

Output:

$ sh test.sh
Hello World! You Input Number Count is 4
1 2 3 4

3.通过指定变量接收参数值

与Shell脚本一样,用户可以在函数中通过$后面接数字的方式获取参数值,需要注意的是$0获取的是脚本的名称。

#!/usr/bin/env bash

#定义函数
func()
{
    echo "$1 $2 $3 $4"
}
func 1 2 3 4

Output:

$ sh test.sh
1 2 3 4

4.移动位置参数

在Shell脚本中用户可以通过shift命令来使所有参数向左移动一个位置,从而使用户可以使用9以内的位置变量获取超过9个的参数:

#!/usr/bin/env bash

#定义函数
func()
{
    while (($# > 0))
    do
        #只是用$1就可以读取所有变量
        echo -n "$1 "
        shift
    done
}

func 1 2 3 4 5 6 7 8 9 10

Output:

$ sh test.sh
1 2 3 4 5 6 7 8 9 10

注意:shift命令会影响$#的值。

5.通过getopts接收函数参数

getopts是bash内置的一个命令,通过命令,用户可以获取函数的选项以及参数值,或者是脚本的命令行选项以及参数值,语法如下: getopts optstring
在上面的语法中,参数optstring包含一个可以为getopts识别的选项名称列表。如果某个选项名称后面有一个冒号,则表示可以为该选项提供参数值。同时参数值被保存在$OPTARG的系统变量中。getopts命令会遍历每个选项,选项名称被保存在args变量中,这个args可以自己起名字,接在optstring后面即可,下面举个例子:

#!/usr/bin/env bash

function Usage()
{
cat << EOF
test [-h] [-f <filename>] [-v <version>] [-d <descfile>]
EOF
}

function GetOpts()
{
while getopts "hf:v:d:" arg  #参数只能由一个字母组成,参数后面接:,表示该参数后面需要接入参数
do
   case "$arg" in
      h)
        Usage
        ;;
      f)
        echo "filename: ${OPTARG}"
        ;;
      v)
        echo "version: ${OPTARG}"
        ;;
      d)
        echo "descfile: ${OPTARG}"
        ;;
      ?)   #当选项不匹配时,varname的值被设置为?
        exit 1
        ;;
   esac
done
}
GetOpts -f test -v v1.0 -d test2
#GetOpts -h

Output:

函数后面接-h
random@random-wz MINGW64 /d/GOCODE/api-test
$ ./test.sh 
test [-h] [-f <filename>] [-v <version>] [-d <descfile>]

函数后面接-f test -v v1.0 -d test2
random@random-wz MINGW64 /d/GOCODE/api-test
$ ./test.sh
filename: test
version: v1.0
descfile: test2

6.间接参数传递

在shell中也支持间接参数传递。所谓间接参数传递是指通过间接变量引用,来实现函数参数的传递,如果一个变量的值是另外一个变量的变量名,则该变量称为间接变量:
example:

#定义两个变量,var的值是name变量的名称
var=name
name=random

则用户可以通过下面两种方式来调用name变量:

${name}
${!var}

7.通过全局变量传递数据

参数的作用是在主程序和函数之间传递数据。但是,用户除了可以使用参数传递数据之外,还可以通过全局变量来传递。但是全局变量的作用域是整个程序,包括函数内部。尽管这种方式是有效的,但是在许多编程语言中,这种方式是不被推荐的,因为它会导致程序结果不清晰,代码可读性变差。

8.传递数组参数

在某些情况下,用户可以将一个数组作为参数传递给某个函数,然后函数对数组内容进行相应的处理。但严格来说,Shell并不支持将数组作为参数传递给函数,但用户可以通过一些变通的方式实现数组参数的传递。
首先,用户可以将数组的元素展开,以空格隔开,然后作为多个由空格隔开的参数传递给函数。

#! /usr/bin/env bash

#定义函数
function func()
{
    echo "number od elements is $#."
    while [ $# -gt 0 ]
    do
        echo "$1"
        shift
    done
}

#定义数组
a=(a b "c d" e)
#注意这里用双引号将数组括起来,如果不括起来,则数组中的"c d"将被认为是两个元素,因为a[@]的值为a b c d e
func "${a[@]}"

Output:

$ sh test.sh
number od elements is 4.
a
b
c d
e

三、库函数文件

1.函数库文件的定义

创建一个函数库文件的过程类似于编写一个Shell脚本。脚本和库文件唯一的区别是库文件中通常只包含函数,而脚本中则既可以还包含函数也可以包含执行的代码。由于函数库文件是由主程序载入并执行,因此用户无需拥有库文件的执行权限,只要拥有读取权限即可。

2. 函数库文件的调用

当库文件定义好之后,用户可以通过下面的方式载入库文件:
. filename
其中filename为库文件的名称,圆点和库文件名称之间有一个空格。
举个例子,我们先创建一个库文件test1.sh,内容如下:

#!/usr/bin/env bash

#输出 Hello World
function HelloWorld()
{
    echo "Hello World"
}

#计算两个数的和
function Sum()
{
    echo $(($1 + $2))
}

在脚本中载入库文件:

#!/usr/bin/env bash

. ./test1.sh

HelloWorld
Sum 1 2

Output:

$ sh test.sh
Hello World
3

四、递归函数

Shell中也支持函数的递归调用,用法和其他编程语言一样,递归函数通过反复的调用本身,直到满足某一个条件退出。这里举一个leetcode上面的题目:
在这里插入图片描述
我们通过shell来解答这道题目:
注意:这里还没有想到不使用if语句如何解答本题目,如果你有好的想法,欢迎评论区交流

#!/usr/bin/env bash

RET=1
function sumNums()
{
     #定义两个变量,n为输入的值,m为n-1
    local n=$1
    local m=$(($n - 1))
    #如果m小于1则停止递归
    if [[ ${m} -lt 1 ]]
    then
        #当递归函数接收到的参数为1则返回1
        RET=1
    else
        #调用递归函数,传递参数m ,其中m=n-1
        sumNums ${m}
        #RET为记录函数返回值的参数,他等于递归函数的返回值加递归函数接收的参数
        # 比如n=2, 则m=1, RET=2 + (sumNums 1),sumNums 1返回1,所以当n=2时,函数返回2+1=3
        RET=$((n + $RET))
    fi
}
sumNums $1
echo ${RET}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章