Linux shell 脚本编程-高级篇 (一)

1. 创建函数


可以将 shell 脚本代码放进函数中封装起来,这样就能在脚本中的任何地方多次使用它了。


1.1 基本的脚本函数


在开始编写较复杂的 shell 脚本时,会发现自己重复使用了部分能够执行特定任务的代码。这些代码有时很简单,比如显示一条文本消息,或者从脚本用户那里获得一个答案;有时则会比较复杂,需要作为大型处理过程中的一部分被多次使用。

在后一类情况下,在脚本中一遍又一遍地编写同样的代码会很烦人。如果能只写一次,随后在脚本中可多次引用这部分代码就好了。

bash shell 提供了这种功能。函数是一个脚本代码块,可以为其命名并在代码中任何位置重用。要在脚本中使用该代码块时,只要使用所起的函数名就行了
(这个过程称为调用函数)。


1.1.1 创建函数
-----------------------------------------------------------------------------------------------------------------------------------------
有两种格式可以用来在 bash shell 脚本中创建函数。第一种格式采用关键字 function,后跟分配给该代码块的函数名。

    function name {
        commands
    }

name 属性定义了赋予函数的唯一名称。脚本中定义的每个函数都必须有一个唯一的名称。
commands 是构成函数的一条或多条 bash shell 命令。在调用该函数时,bash shell 会按命令在函数中出现的顺序依次执行,就像在普通脚本中一样。

在 bash shell 脚本中定义函数的第二种格式更接近于其他编程语言中定义函数的方式:

    name() {
        commands
    }

函数名后的空括号表明正在定义的是一个函数。这种格式的命名规则和之前定义 shell 脚本函数的格式一样。


1.1.2 使用函数
-----------------------------------------------------------------------------------------------------------------------------------------
要在脚本中使用函数,只需要像其他 shell 命令一样,在行中指定函数名就行了。

示例:
    [devalone@devalone 17]$ cat test1.sh
    #/bin/bash
    # using a function in a script

    function func1 {
            echo "This is an example of function"
    }

    count=1
    while [ $count -le 5 ]
    do
            func1
            count=$[ $count + 1 ]
    done

    echo "This is the end of the loop"
    func1
    echo "Now this is the end of the script"

运行:
    [devalone@devalone 17]$ chmod a+x test1.sh
    [devalone@devalone 17]$ ./test1.sh
    This is an example of function
    This is an example of function
    This is an example of function
    This is an example of function
    This is an example of function
    This is the end of the loop
    This is an example of function
    Now this is the end of the script


每次引用函数名 func1 时,bash shell 会找到 func1 函数的定义并执行在那里定义的命令。

函数定义不一定非得是 shell 脚本中首先要做的事,但一定要小心。如果在函数被定义前使用函数,会收到一条错误消息。

示例:
    [devalone@devalone 17]$ cat test2.sh
    #/bin/bash
    # using a function located in the middle of the script

    echo "This line comes before the function definition"
    function func1 {
            echo "This is an example of function"
    }

    count=1
    while [ $count -le 5 ]
    do
            func1
            count=$[ $count + 1 ]
    done

    echo "This is the end of the loop"
    func2
    echo "Now this is the end of the script"

    function func2 {
            echo "This is an example of a function"
    }

运行:
    [devalone@devalone 17]$ test2.sh
    This line comes before the function definition
    This is an example of function
    This is an example of function
    This is an example of function
    This is an example of function
    This is an example of function
    This is the end of the loop
    ./test2.sh:行17: func2: 未找到命令
    Now this is the end of the script

第一个函数 func1 的定义出现在脚本中的几条语句之后,这当然没任何问题。当 func1 函数在脚本中被使用时,shell 知道去哪里找它。

然而,脚本试图在 func2 函数被定义之前使用它。由于 func2 函数还没有定义,脚本运行函数调用处时,产生了一条错误消息。

也必须注意函数名。记住,函数名必须是唯一的,否则也会有问题。如果重定义了函数,新定义会覆盖原来函数的定义,这一切不会产生任何错误消息。

示例:
    [devalone@devalone 17]$ cat test3.sh
    #/bin/bash
    # test using a duplicate function name

    function func1 {
            echo "This is the first definition of the function name"
    }

    func1

    function func1 {
            echo "This is a repeat fo the same function name"
    }

    func1
    echo "This is the end of the script"

运行:
    [devalone@devalone 17]$ chmod a+x test3.sh
    [devalone@devalone 17]$ test3.sh
    This is the first definition of the function name
    This is a repeat fo the same function name
    This is the end of the script

func1 函数最初的定义工作正常,但重新定义该函数后,后续的函数调用都会使用第二个定义。


1.2 返回值
-----------------------------------------------------------------------------------------------------------------------------------------
bash shell 会把函数当作一个小型脚本,运行结束时会返回一个退出状态码。有 3 种不同的方法来为函数生成退出状态码。


1.2.1 默认退出状态码
-----------------------------------------------------------------------------------------------------------------------------------------
默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以用标准变量 $? 来确定函数的退出状态码。

示例:
    [devalone@devalone 17]$ cat test4.sh
    #!/bin/bash
    # testing the exit status of a function

    func1 () {
        echo "tryint to display a non-existent file"
        ls -l badfile
    }

    echo "testing the function"
    func1
    echo "The exit code status is: $?"

运行:
    [devalone@devalone 17]$ chmod a+x test4.sh
    [devalone@devalone 17]$ test4.sh
    testing the function
    tryint to display a non-existent file
    ls: 无法访问'badfile': No such file or directory
    The exit code status is: 2

函数的退出状态码是 2,这是因为函数中的最后一条命令没有成功运行。但无法知道函数中其他命令中是否成功运行。

示例:
    [devalone@devalone 17]$ cat test4b.sh
    #!/bin/bash
    # testing the exit status of a function
    func1() {
        ls -l badfile
        echo "This was a test of a bad command"
    }
    echo "testing the function:"
    func1
    echo "The exit status is: $?"

运行:
    [devalone@devalone 17]$ chmod a+x test4b.sh
    [devalone@devalone 17]$ test4b.sh
    testing the function:
    ls: 无法访问'badfile': No such file or directory
    This was a test of a bad command
    The exit status is: 0

这次,由于函数最后一条语句 echo 运行成功,该函数的退出状态码就是 0,尽管其中有一条命令并没有正常运行。使用函数的默认退出状态码是很危险的。
幸运的是,有几种办法可以解决这个问题。


1.2.2 使用 return 命令
-----------------------------------------------------------------------------------------------------------------------------------------
bash shell 使用 return 命令来退出函数并返回特定的退出状态码。return 命令允许指定一个整数值来定义函数的退出状态码,从而提供了一种简单的途径
来编程设定函数退出状态码。

示例:
    [devalone@devalone 17]$ cat test5.sh
    #!/bin/bash
    # using the return command in a funcion

    function db1 {
        read -p "Enter a value: " value
        echo "doubling the value"
        return $[ $value * 2 ]
    }

    db1
    echo "The new value is $?"

dbl函数会将 $value 变量中用户输入的值翻倍,然后用 return 命令返回结果。脚本用 $? 变量显示了该值。

但当用这种方法从函数中返回值时,要小心了。记住下面两条技巧来避免问题:

    □ 记住,函数一结束就取返回值;
    □ 记住,退出状态码必须是 0~255。

如果在用 $? 变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失。记住,$? 变量会返回执行的最后一条命令的退出状态码。

第二个问题界定了返回值的取值范围。由于退出状态码必须小于 256,函数的结果必须生成一个小于 256 的整数值。任何大于256的值都会产生一个错误值。

运行:
    [devalone@devalone 17]$ test5.sh
    Enter a value: 5
    doubling the value
    The new value is 10
    
    [devalone@devalone 17]$ test5.sh
    Enter a value: 200
    doubling the value
    The new value is 144


1.2.3 使用函数输出
-----------------------------------------------------------------------------------------------------------------------------------------
正如可以将命令的输出保存到 shell 变量中一样,也可以对函数的输出采用同样的处理办法。可以用这种技术来获得任何类型的函数输出,并将其保存到
变量中:

    result=`dbl`

这个命令会将 dbl 函数的输出赋给 $result 变量。

示例:

    [devalone@devalone 17]$ cat test5b.sh
    #!/bin/bash
    # using the echo to return a value

    function db1 {
        read -p "Enter a value: " value
        echo $[ $value * 2 ]
    }

    result=$(db1)
    echo "The new value is $result"

运行:
    [devalone@devalone 17]$ chmod a+x test5b.sh
    [devalone@devalone 17]$ test5b.sh
    Enter a value: 200
    The new value is 400

新函数用 echo 语句来显示计算的结果。该脚本会获取 dbl 函数的输出,而不是查看退出状态码。

这个例子中演示了一个不易察觉的技巧。dbl 函数实际上输出了两条消息。read 命令输出了一条简短的消息来向用户询问输入值。bash shell脚本非常聪明,
并不将其作为 STDOUT 输出的一部分,并且忽略掉它。如果用 echo 语句生成这条消息来向用户查询,那么它会与输出值一起被读进 shell 变量中。

    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    通过这种技术,还可以返回浮点值和字符串值。这使它成为一种获取函数返回值的强大方法。


1.3 在函数中使用变量
-----------------------------------------------------------------------------------------------------------------------------------------
在函数中使用变量时,需要注意它们的定义方式以及处理方式。这是 shell 脚本中常见错误的根源。


1.3.1 向函数传递参数
-----------------------------------------------------------------------------------------------------------------------------------------
bash shell 会将函数当作小型脚本来对待。这意味着可以像普通脚本那样向函数传递参数。

函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如,函数名会在 $0 变量中定义,函数命令行上的任何参数都会通过 $1、$2 等定义。
也可以用特殊变量 $# 来判断传给函数的参数数目。

在脚本中指定函数时,必须将参数和函数放在同一行,像这样:

    func1 $value1 10

然后函数可以用参数环境变量来获得参数值。

示例:
    [devalone@devalone 17]$ cat test6.sh
    #!/bin/bash
    # passing paramters to a function

    function addem {
        if [ $# -eq 0 ] || [ $# -gt 2 ]
        then
            echo -1
        elif [ $# -eq 1 ]
        then
            echo $[ $1 + $1 ]
        else
            echo $[ $1 + $2 ]
        fi
    }

    echo -n "Adding 10 and 15: "
    value=$(addem 10 15)
    echo $value
    echo -n "Let's try adding just one number:"
    value=$(addem 10)
    echo $value
    echo -n "Now trying adding no numbers:"
    value=$(addem)
    echo $value
    echo -n "Finially, try adding three numbers:"
    value=$(addem 10 15 20)
    echo $value

运行:
    [devalone@devalone 17]$ test6.sh
    Adding 10 and 15: 25
    Let's try adding just one number:20
    Now trying adding no numbers:-1
    Finially, try adding three numbers:-1

text6.sh 脚本中的 addem 函数首先会检查脚本传给它的参数数目。如果没有任何参数,或者参数多于两个,addem 会返回值 -1。如果只有一个参数,
addem 会将参数与自身相加。如果有两个参数,addem 会将它们进行相加。

由于函数使用特殊参数环境变量作为自己的参数值,因此它无法直接获取脚本在命令行中的参数值。下面的例子将会运行失败:

    [devalone@devalone 17]$ cat badtest1.sh
    #!/bin/bash
    # trying to access script parameters inside a function

    function badfunc1 {
        echo $[ $1 * $2 ]
    }

    if [ $# -eq 2 ]
    then
        value=$(badfunc1)
        echo "The result is $value"
    else
        echo "Usage: badtest1 a b"
    fi

运行:
    [devalone@devalone 17]$ chmod a+x badtest1.sh
    [devalone@devalone 17]$ badtest1.sh
    Usage: badtest1 a b
    [devalone@devalone 17]$ badtest1.sh 10 15
    ./badtest1.sh:行5: *  : 语法错误: 需要操作数 (错误符号是 "*  ")
    The result is

尽管函数也使用了 $1 和 $2 变量,但它们和脚本主体中的$1和$2变量并不相同。要在函数中使用这些值,必须在调用函数时手动将它们传过去。

示例:
    [devalone@devalone 17]$ cat test7.sh
    #!/bin/bash
    # trying to access script parameters inside a function

    function func7 {
        echo $[ $1 * $2 ]
    }

    if [ $# -eq 2 ]
    then
        value=$(func7 $1 $2)
        echo "The result is $value"
    else
        echo "Usage: test7 a b"
    fi

运行:
    [devalone@devalone 17]$ chmod a+x test7.sh
    [devalone@devalone 17]$ test7.sh
    Usage: test7 a b
    [devalone@devalone 17]$ test7.sh 10 15
    The result is 150

通过将 $1 和 $2 变量传给函数,它们就能跟其他变量一样供函数使用了。


1.3.2 在函数中处理变量
-----------------------------------------------------------------------------------------------------------------------------------------
给 shell 脚本程序员带来麻烦的原因之一就是变量的作用域。作用域是变量可见的区域。函数中定义的变量与普通变量的作用域不同。也就是说,对脚本的
其他部分而言,它们是隐藏的。

函数使用两种类型的变量:

    □ 全局变量
    □ 局部变量

■ 全局变量
-----------------------------------------------------------------------------------------------------------------------------------------
全局变量是在 shell 脚本中任何地方都有效的变量。如果在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。类似地,如果在函数内定
义了一个全局变量,可以在脚本的主体部分读取它的值。

默认情况下,在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问。

示例:
    [devalone@devalone 17]$ cat test8.sh
    #!/bin/bash
    # using a global variable to pass a value

    function db1 {
        value=$[ $value * 2 ]
    }

    read -p "Enter a value: " value
    db1
    echo "the new value is: $value"

运行:    
    [devalone@devalone 17]$ chmod a+x test8.sh
    [devalone@devalone 17]$ test8.sh
    Enter a value: 400
    the new value is: 800

$value 变量在函数外定义并被赋值。当 dbl 函数被调用时,该变量及其值在函数中都依然有效。如果变量在函数内被赋予了新值,那么在脚本中引用该
变量时,新值也依然有效。

但这其实很危险,尤其是如果想在不同的 shell 脚本中使用函数的话。它要求清清楚楚地知道函数中具体使用了哪些变量,包括那些用来计算非返回值的
变量。下面的例子可说明事情是如何搞砸的:

    [devalone@devalone 17]$ cat badtest2.sh
    #!/bin/bash
    # demonstrating a bad use of variables

    function func1 {
        temp=$[ $value + 5 ]
        result=$[ $temp * 2 ]
    }

    temp=4
    value=6

    func1

    echo "The result is $result"
    if [ $temp -gt $value ]
    then
        echo "temp is larger"
    else
        echo "temp is smaller"
    fi

运行:
    [devalone@devalone 17]$ chmod a+x badtest2.sh
    [devalone@devalone 17]$ badtest2.sh
    The result is 22
    temp is larger

由于函数中用到了 $temp变量,它的值在脚本中使用时受到了影响,产生了意想不到的后果。


■ 局部变量
-----------------------------------------------------------------------------------------------------------------------------------------
无需在函数中使用全局变量,函数内部使用的任何变量都可以被声明成局部变量。要实现这一点,只要在变量声明的前面加上 local 关键字就可以了。

    local temp

也可以在变量赋值语句中使用 local 关键字:

    local temp=$[ $value + 5 ]

local 关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量,那么 shell 将会保持这两个变量的值是分离的。现在就能很轻松
地将函数变量和脚本变量隔离开了,只共享需要共享的变量。

示例:
    [devalone@devalone 17]$ cat test9.sh
    #!/bin/bash
    # demonstrating the local keyword

    function func1 {
        local temp=$[ $value + 5 ]
        result=$[ $temp * 2 ]
    }

    temp=4
    value=6

    func1
    echo "The result is $result"
    if [ $temp -gt $value ]
    then
        echo "temp is larger"
    else
        echo "temp is smaller"
    fi

运行:
    [devalone@devalone 17]$ chmod a+x test9.sh
    [devalone@devalone 17]$ test9.sh
    The result is 22
    temp is smaller

现在,在 func1 函数中使用 $temp 变量时,并不会影响在脚本主体中赋给 $temp 变量的值。


1.4 数组变量和函数
-----------------------------------------------------------------------------------------------------------------------------------------
在函数中使用数组变量值有点麻烦,而且还需要一些特殊考虑。


1.4.1 向函数传数组参数
-----------------------------------------------------------------------------------------------------------------------------------------
向脚本函数传递数组变量的方法会有点不好理解。将数组变量当作单个参数传递的话,它不会起作用。

示例:

    [devalone@devalone 17]$ cat badtest3.sh
    #!/bin/bash
    # trying to pass an array variable

    function testit {
            echo "The parameters are: $@"
            thisarray=$1
            echo "The received array is ${thisarray[*]}"
    }

    myarray=(1 2 3 4 5)
    echo "The original array is: ${myarray[*]}"
    testit $myarray

运行:
    [devalone@devalone 17]$ chmod a+x badtest3.sh
    [devalone@devalone 17]$ badtest3.sh
    The original array is: 1 2 3 4 5
    The parameters are: 1
    The received array is 1

如果试图将该数组变量作为函数参数,函数只会取数组变量的第一个值。

要解决这个问题,必须将该数组变量的值分解成单个的值,然后将这些值作为函数参数使用。在函数内部,可以将所有的参数重新组合成一个新的变量。

示例:
    [devalone@devalone 17]$ cat test10.sh
    #!/bin/bash
    # array variable to function test

    function testit {
        local newarray
        newarray=(`echo "$@"`)
        echo "The new array value is: ${newarray[*]}"
    }

    myarray=(1 2 3 4 5)
    echo "The original array is ${myarray[*]}"
    testit ${myarray[*]}

运行:
    [devalone@devalone 17]$ chmod a+x test10.sh
    [devalone@devalone 17]$ test10.sh
    The original array is 1 2 3 4 5
    The new array value is: 1 2 3 4 5

该脚本用 $myarray 变量来保存所有的数组元素,然后将它们都放在函数的命令行上。该函数随后从命令行参数中重建数组变量。在函数内部,数组仍然
可以像其他数组一样使用。

示例:
    [devalone@devalone 17]$ cat test11.sh
    #!/bin/bash
    # adding values in an array

    function addarray {
        local sum=0
        local newarray
        newarray=($(echo "$@"))
        for value in ${newarray[*]}
        do
                sum=$[ $sum + $value ]
        done

        echo $sum
    }

    myarray=(1 2 3 4 5)
    echo "The original array is ${myarray[*]}"

    arg1=$(echo ${myarray[*]})
    result=$(addarray $arg1)
    echo "The result is $result"

运行:
    [devalone@devalone 17]$ chmod a+x test11.sh
    [devalone@devalone 17]$ test11.sh
    The original array is 1 2 3 4 5
    The result is 15


addarray 函数会遍历所有的数组元素,将它们累加在一起。可以在 myarray 数组变量中放置任意多的值,addarry 函数会将它们都加起来。


1.4.2 从函数返回数组
-----------------------------------------------------------------------------------------------------------------------------------------
从函数里向 shell 脚本传回数组变量也用类似的方法。函数用 echo 语句来按正确顺序输出单个数组值,然后脚本再将它们重新放进一个新的数组变量中。

示例:
    [devalone@devalone 17]$ cat test12.sh
    #!/bin/bash
    # return an array value

    function arraydblr {
        local origarray
        local newarray
        local elements
        local i
        origarray=($(echo "$@"))
        newarray=($(echo "$@"))
        elements=$[ $#-1 ]
        for ((i=0; i<= $elements; i++))
            {
                newarray[$i]=$[ ${origarray[$i]} * 2 ]
            }
            echo ${newarray[*]}

    }

    myarray=(1 2 3 4 5)
    echo "The original array is: ${myarray[*]}"
    arg1=$(echo ${myarray[*]})
    result=($(arraydblr $arg1))
    echo "The new array is: ${result[*]}"

运行:
    [devalone@devalone 17]$ chmod a+x test12.sh
    [devalone@devalone 17]$ test12.sh
    The original array is: 1 2 3 4 5
    The new array is: 2 4 6 8 10

该脚本用 $arg1 变量将数组值传给 arraydblr 函数。arraydblr 函数将该数组重组到新的数组变量中,生成该输出数组变量的一个副本。然后对数据元素
进行遍历,将每个元素值翻倍,并将结果存入函数中该数组变量的副本。

arraydblr 函数使用 echo 语句来输出每个数组元素的值。脚本用 arraydblr 函数的输出来重新生成一个新的数组变量。


1.5 函数递归
-----------------------------------------------------------------------------------------------------------------------------------------
局部函数变量的一个特性是自成体系。除了从脚本命令行处获得的变量,自成体系的函数不需要使用任何外部资源。

这个特性使得函数可以递归地调用,也就是说,函数可以调用自己来得到结果。通常递归函数都有一个最终可以迭代到的基准值。许多高级数学算法用递归
对复杂的方程进行逐级规约,直到基准值定义的那级。

递归算法的经典例子是计算阶乘。一个数的阶乘是该数之前的所有数的乘积乘以该数的值。因此,要计算5的阶乘,可以执行如下方程:

    5! = 1 * 2 * 3 * 4 * 5 = 120

使用递归,方程可以简化成以下形式:
    
    x! = x * (x-1)!
    
也就是说,x 的阶乘等于 x 乘以 x-1 的阶乘。这可以用简单的递归脚本表达为:

    function factorial {
        if [ $1 -eq 1 ]
        then
            echo 1
        else
            local temp=$[ $1 - 1 ]
            local result='factorial $temp'
            echo $[ $result * $1 ]
        fi
    }    

阶乘函数用它自己来计算阶乘的值:
    [devalone@devalone 17]$ cat test13.sh
    #!/bin/bash
    # using recursion

    function factorial
    {
            if [ $1 -eq 1 ]
            then
                    echo 1
            else
                    local temp=$[ $1 - 1 ]
                    local result=$(factorial $temp)
                    echo $[ $result * $1 ]
            fi
    }

    read -p "Enter value: " value
    result=$(factorial $value)
    echo "The factorial of $value is: " $result

运行:
    [devalone@devalone 17]$ chmod a+x test13.sh
    [devalone@devalone 17]$ test13.sh
    Enter value: 5
    The factorial of 5 is:  120


1.6 创建库
-----------------------------------------------------------------------------------------------------------------------------------------
使用函数可以在脚本中省去一些输入工作,这一点是显而易见的。但如果碰巧要在多个脚本中使用同一段代码呢?显然,为了使用一次而在每个脚本中都
定义同样的函数太过麻烦。

有个方法能解决这个问题!bash shell 允许创建函数库文件,然后在多个脚本中引用该库文件。

这个过程的第一步是创建一个包含脚本中所需函数的公用库文件。这里有个叫作 myfuncs 的库文件,它定义了 3 个简单的函数。

示例:
    [devalone@devalone 17]$ cat myfuncs
    # my script functions
    function addem {
             echo $[ $1 + $2 ]
    }

    function multem {
            echo $[ $1 * $2 ]
    }

    function divem {
             if [ $2 -ne 0 ]
             then
                    echo $[ $1 / $2 ]
             else
                    echo -1
             fi
    }
    [devalone@devalone 17]$ chmod a+x myfuncs

下一步是在用到这些函数的脚本文件中包含 myfuncs 库文件。从这里开始,事情就变复杂了。

问题出在 shell 函数的作用域上。和环境变量一样,shell 函数仅在定义它的 shell 会话内有效。如果在 shell 命令行界面的提示符下运行 myfuncs shell 脚本,shell 会创建一个新的 shell 并在其中运行这个脚本。它会为那个新 shell 定义这三个函数,但当运行另外一个要用到这些函数的脚本时,它们是无法使用的。

这同样适用于脚本。如果尝试像普通脚本文件那样运行库文件,函数并不会出现在脚本中。

示例:
    [devalone@devalone 17]$ cat badtest4.sh
    #!/bin/bash
    # using a library file the wrong way

    ./myfuncs

    result=$(addem 10 15)
    echo "The result is $result"

运行:
    [devalone@devalone 17]$ chmod a+x badtest4.sh
    [devalone@devalone 17]$ badtest4.sh
    ./badtest4.sh:行6: addem: 未找到命令
    The result is

使用函数库的关键在于 source 命令。source 命令会在当前 shell 上下文中执行命令,而不是创建一个新 shell。可以用 source 命令来在 shell 脚本中运行库文件脚本。这样脚本就可以使用库中的函数了。

source 命令有个快捷的别名,称作点操作符(dot operator)。要在 shell 脚本中运行 myfuncs 库文件,只需添加下面这行:

    . ./myfuncs

这个例子假定 myfuncs 库文件和 shell 脚本位于同一目录。如果不是,需要使用相应路径访问该文件。

示例:
    [devalone@devalone 17]$ cat test14.sh
    #!/bin/bash
    # using functions defined in a library file

    . ./myfuncs

    value1=10
    value2=5

    result1=$(addem $value1 $value2)
    result2=$(multem $value1 $value2)
    result3=$(divem $value1 $value2)

    echo "The result of adding them is: " $result1
    echo "The result of multiplying them is: " $result2
    echo "The result of dividing them is: " $result3

运行:
    [devalone@devalone 17]$ chmod a+x test14.sh
    [devalone@devalone 17]$ test14.sh
    The result of adding them is:  15
    The result of multiplying them is:  50
    The result of dividing them is:  2

该脚本成功地使用了 myfuncs 库文件中定义的函数。


1.7 在命令行上使用函数
-----------------------------------------------------------------------------------------------------------------------------------------
可以用脚本函数来执行一些十分复杂的操作。有时也很有必要在命令行界面的提示符下直接使用这些函数。

和在 shell 脚本中将脚本函数当命令使用一样,在命令行界面中也可以这样做。因为一旦在 shell 中定义了函数,就可以在整个系统中使用它了,无需担心脚本是不是在 PATH 环境变量里。重点在于让 shell 能够识别这些函数。有几种方法可以实现。


1.7.1 在命令行上创建函数
-----------------------------------------------------------------------------------------------------------------------------------------
因为 shell 会解释用户输入的命令,所以可以在命令行上直接定义一个函数。有两种方法。

一种方法是采用单行方式定义函数:

    [devalone@devalone 17]$ function divem { echo $[ $1 / $2 ]; }
    [devalone@devalone 17]$ divem 100 5
    20
    
当在命令行上定义函数时,必须记得在每个命令后面加个分号,这样 shell 就能知道在哪里是命令的起止了。

示例:
    [devalone@devalone 17]$ function doubleit { read -p "Enter value: " value; echo $[ $value * 2 ]; }
    [devalone@devalone 17]$ doubleit
    Enter value: 20
    40

另一种方法是采用多行方式来定义函数。在定义时,bash shell 会使用次提示符来提示输入更多命令。用这种方法,不用在每条命令的末尾放一个分号,只
要按下回车键就行。

示例:
    [devalone@devalone 17]$ function multem {
    > echo $[ $1 * $2 ]
    > }
    [devalone@devalone 17]$ multem 2 5
    10

在函数的尾部使用花括号,shell 就会知道已经完成了函数的定义。


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    在命令行上创建函数时要特别小心。如果给函数起了个跟内建命令或另一个命令相同的名字,函数将会覆盖原来的命令。
    
    
1.7.2 在.bashrc 文件中定义函数
-----------------------------------------------------------------------------------------------------------------------------------------
在命令行上直接定义 shell 函数的明显缺点是退出 shell时,函数就消失了。对于复杂的函数来说,这可是个麻烦事。

个非常简单的方法是将函数定义在一个特定的位置,这个位置在每次启动一个新 shell 的时候,都会由 shell 重新载入。

最佳地点就是 .bashrc 文件。bash shell 在每次启动时都会在主目录下查找这个文件,不管是交互式 shell 还是从现有 shell 中启动的新 shell。


■ 直接定义函数
-----------------------------------------------------------------------------------------------------------------------------------------
可以直接在主目录下的 .bashrc 文件中定义函数。许多 Linux 发行版已经在 .bashrc文件中定义了一些内容,所以注意不要误删了。把自己写的函数放在
文件末尾就行了。

示例:
    [devalone@devalone 17]$ cat ~/.bashrc
    # .bashrc

    # Source global definitions
    if [ -f /etc/bashrc ]; then
            . /etc/bashrc
    fi

    # Uncomment the following line if you don't like systemctl's auto-paging feature:
    # export SYSTEMD_PAGER=

    # User specific aliases and functions


    function addem {
            echo $[ $1 + $2 ]
    }

该函数会在下次启动新 bash shell 时生效。随后就能在系统上任意地方使用这个函数了。


■ 读取函数文件
-----------------------------------------------------------------------------------------------------------------------------------------
只要是在 shell 脚本中,都可以用 source 命令(或者它的别名点操作符)将库文件中的函数添加到的 .bashrc 脚本中。

示例:
    [devalone@devalone 17]$ cat ~/.bashrc
    # .bashrc

    # Source global definitions
    if [ -f /etc/bashrc ]; then
            . /etc/bashrc
    fi

    # Uncomment the following line if you don't like systemctl's auto-paging feature:
    # export SYSTEMD_PAGER=

    # User specific aliases and functions

    export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'

    . /home/devalone/study/shell-script/17/myfuncs

运行:
    [devalone@devalone 17]$ bash
    [devalone@devalone 17]$ addem 10 5
    15
    [devalone@devalone 17]$ multem 10 5
    50
    [devalone@devalone 17]$ divem 10 5
    2
    [devalone@devalone 17]$ exit
    exit

更好的是,shell 还会将定义好的函数传给子shell 进程,这样一来,这些函数就自动能够用于该 shell 会话中的任何 shell 脚本了。
示例:
    [devalone@devalone 17]$ cat test15.sh
    #!/bin/bash
    # using a function defined in the .bashrc file
    value1=10
    value2=5
    result1=$(addem $value1 $value2)
    result2=$(multem $value1 $value2)
    result3=$(divem $value1 $value2)
    echo "The result of adding them is: $result1"
    echo "The result of multiplying them is: $result2"
    echo "The result of dividing them is: $result3"

运行:
    $ ./test15
    The result of adding them is: 15
    The result of multiplying them is: 50
    The result of dividing them is: 2

 

系列目录:

Linux shell 脚本编程-高级篇 (一)

Linux shell 脚本编程-高级篇 (二)

Linux shell 脚本编程-高级篇 (三)

Linux shell 脚本编程-高级篇 (四)

Linux shell 脚本编程-高级篇 (五)

Linux shell 脚本编程-高级篇 (六)

Linux shell 脚本编程-高级篇 (七)

 

 

-----------------------------------------------------------------------------------------------------------------------------------------
参考:

    《Linux 命令行与 shell 脚本编程大全》 第 3 版 —— 2016.8(美)Richard Blum  Cristine Bresnahan

你可能感兴趣的:(Linux,操作系统,CentOS7,Fedora)