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

linux高级编程 成功 FILE*指针 //文件指针 流指针 --- 从程序上讲,FILE *指针就代表打开的这个文件。stderr //标准出错 --- 屏幕 --- 可以专门把错误信息输出到 stderr。@pathname --- 要打开的文件名 //字符串形式的名字。@c //要输出的字符 ---ascii码值。文件IO --- 安全性 操作的是硬件 ---文件IO。@s //存放读取到的数据 对应的内存空间。1.打开 -- fopen //file open。 阅读详情

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 Shell脚本编程大全:入门到高级技巧 Linux Shell脚本是一种用于自动化常规任务的脚本语言。它允许用户通过编写一系列命令来控制Linux系统的行为。Shell脚本经常被系统管理员和开发人员用于软件部署、系统监控、任务调度等场景。理解Shell脚本的基本工作原理和结构对于任何在Linux环境下工作的IT专业人员来说,都是一个重要的技能点。变量的定义和赋值是编写脚本时的基础操作。它们为脚本提供了可操作的数据。函数是一组命令的集合,可以被重复调用执行。在Shell脚本中,函数的使用提高了代码的复用性和可维护性。 阅读详情

相关推荐

shell高级编程(文档+源码).zip

shell高级编程(文档+源码) shell高级编程(带365个实例源码) shell高级编程(含365个经典实例源码)

linux中,如何写一个shell脚本

直接赋值跟所有的编程语言一样,变量就是用来赋值的名称。变量赋值跟Java编程语言保持一致变量名=“赋予的值”,为变量赋值时可以使用单引号也可以使用双引号age = "18"⚠️ 变量名和等号之间不能有空格,变量名的命名需要遵守以下规则变量名可包含字母、数字、下划线_数字不能作为变量名的开头不能包含特殊字符(?!@ & *……)不能使用shell关键字(if、then、while、else、for……)作为变量名避免造成不必要的误会用全大写字母表示常量。

achaoyang的博客 5906

结合 Conda 手动安装 RepeatMasker

RepeatMasker 是一个广泛使用的生物信息学工具,用于识别和屏蔽(mask)基因组序列中的 重复序列(repeats) 。在使用过程中需要调用重复序列数据库(Repbase 和 Dfam),如果使用Conda安装的话会出现许多问题,因此推荐手动安装。在此记录我手动安装RepeatMasker的流程,希望对各位同学有所帮助。

m0_65437087的博客 2536

Linux之shell脚本编程

•。

weixin_57975238的博客 3088

C++,使用Qt设计一个登录窗口

使用Qt设计一个登录界面

heyu0311的博客 2991

GCJ02坐标系转WGS84坐标系(已实地测试基本无偏差)

坐标系转化,GCJ02坐标系,WGS84坐标系

Xiao_XianYu_的博客 1859

linux shell高级脚本,Shell 脚本编程(高级篇)

高级篇一、处理用户输入1. 读取脚本参数Bash Shell 将命令行中传递给脚本的参数赋值给一组特殊的变量,叫做位置变量(positional parameters)。位置变量用 $number 的形式表示。如 $0 表示脚本文件的名称,$1 表示脚本收到的第一个参数,$2 表示第二个参数,以此类推,直到 $9 表示第九个参数。从第十个参数起,使用 ${number} 的形式。即第十个参数表示为...

weixin_29080033的博客 637

数据分析案例-基于红米和华为手机的用户评论分析

在智能手机市场日益繁荣的今天,红米与华为手机作为国产手机品牌的佼佼者,凭借其各自独特的品牌魅力和技术实力,赢得了广泛的用户群体和高度关注。随着技术的不断进步和消费者需求的日益多样化,用户对手机的期望已不仅限于基本的通讯功能,更涵盖了性能、拍照、续航、用户体验等多个方面。因此,深入探究红米与华为手机在用户长期使用过程中的实际表现,尤其是用户评论中所反映出的真实反馈,对于理解市场需求、优化产品设计及提升用户体验具有重要意义。本实验的背景正是基于这样的市场环境和技术发展趋势。我们旨在通过收集并分析红米与华为手机的

m0_64336780的博客 1万+

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

继 Linux shell 脚本编程-高级篇 (六) 7. 使用其他 shell 虽然 bash shell 是 Linux 发行版中最广泛使用的 shell,但它并不是唯一的选择。本节介绍另外两个可能会碰到的 shell: dash 和 zsh 。 7.1 什么是 dash shell Debian 的 dash shell 是 ash shell 的直系后代,而 ash shell...

devalone 的专栏 2863

Win11 系统下 VisualStudio2022 配置点云库 PCL1.11.1

安装完成之后打开文件夹E:\PCL 1.11.1\3rdParty\OpenNI2双击 并选择路径 (E:\PCL 1.11.1\3rdParty\OpenNI2) 安装即可全部安装完成之后,将pcl-1.11.1-pdb-msvc2019-win64.zip解压后的 .pdb文件拷贝到(E:\PCL 1.11.1\bin)中设置环境变量:到此,环境变量配置完成。

qq_53545309的博客 1128

SpringBoot+Vue+kkFileView实现文档管理(文档上传、下载、在线预览)

上面在使用OpenOffice实现doc、excel、ppt等文档的管理和预览。除此之外可用kkFileView实现包括且更多文档的预览。

BADAO_LIUMANG_QIZHI的博客 1万+

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

继 Linux shell 脚本编程-高级篇 (一) 2. 图形化桌面环境中的脚本编程   2.1 创建文本菜单 创建交互式 shell 脚本最常用的方法是使用菜单。提供各种选项可以帮助脚本用户了解脚本能做什么和不能做什么。   通常菜单脚本会清空显示区域,然后显示可用的选项列表。用户可以按下与每个选项关联的字母或数字来选择选项。 shell 脚本菜单的核心是 case 命令。ca...

devalone 的专栏 1898

GB28181 —— 3、C++编写GB28181设备端,完成注册、注销、保活心跳、响应查询目录等功能(附源码)

GB28181 —— Win10下编写GB28181设备端,完成注册、注销、保活心跳、响应查询目录等功能(附源码)

1310

linux shell高级脚本编程之函数篇

1.创建函数 function name

night_wisp的专栏 635

Android AVB 分析(十三)dm-verity 设备是如何映射的?

在上一篇《Android AVB 分析(十二)嵌入式设备安全中的 dm-verity 简介》介绍 dm-verity 原理的时候,作者提供了一个 dm-verity 演示的例子。本篇我们基于 AOSP 源码 `android-13.0.0_r41` 实际编译生成的 Google Pixel 7 (“panther”) 设备的 system 分区镜像进行 dm-verity 设备验证实战。

洛奇看世界 2440

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

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

继 Linux shell 脚本编程-高级篇 (三)   4. 正则表达式 在 shell 脚本中成功运用 sed 编辑器和 gawk 程序的关键在于熟练使用正则表达式。这可不是件简单的事,从大量数据中过滤出特定数据可能会(而且经常会)很复杂。 4.1 什么是正则表达式 4.1.1 定义 正则表达式是所定义的模式模板(pattern template),Linux 工具可以用它来...

devalone 的专栏 1397

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

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

继: Linux shell 脚本编程-高级篇 (四) 5. sed 进阶 sed 编辑器的基础命令能满足大多数日常文本编辑需求。本节介绍 sed 编辑器提供的更多高级特性。这些功能未必会经常用到,但当需要时,知道这些功能 的存在以及如何使用它们是必要的。 5.1 多行命令 ---------------------------------------------------------...

devalone 的专栏 922

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

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

继 Linux shell 脚本编程-高级篇 (二)   3. 初识sed和gawk 目前为止, shell 脚本最常见的一个用途就是处理文本文件。检查日志文件、读取配置文件、处理数据元素,shell 脚本可以帮助我们将文本文件中各种数据的日常处理任务自动化。但仅靠 shell 脚本命令来处理文本文件的内容有点勉为其难。如果想在 shell 脚本中处理任何类型的数据,需要熟悉Linux中的...

devalone 的专栏 1853

linux 学习资料、Linux学习书籍(入门书籍、shell编程)推荐

这里推荐几本学习linux书籍,好的教程,可以让学习者少走弯路。 可以把枯燥的东西变得有乐趣。不是吗? 第一本推荐入门级别书籍: 鸟哥的Linux私房菜 基础学习篇(第3版) 它对linux讲解,由浅入深,一定会让很多初学者爱不释手的。 第二本推荐shell入门级别书籍: LINUX与UNIX SHELL编程指南 这个讲解了shell一些 几本命令,让初学者领略到shell强大。一定会增加...

weixin_30485291的博客 239

Linux Shell 脚本编程基础知识篇—shell简介以及基本操作

Shell本身是一个用C语言编写的程序, 它是用户使用Linux的桥梁。Shell既是一种命令语言, 又是一种程序设计语言。所有的Linux命令都是Shell脚本语言。

weixin_55767624的博客 2723

Linux 高级 Shell 脚本编程:掌握 Shell 脚本精髓,提升工作效率

函数体高级 Shell 脚本编程是提升 Shell 脚本编程技能的关键。通过学习函数、数组、高级控制结构和脚本调试技巧,你可以编写更高效、更灵活和更易于维护的脚本。掌握这些技能可以帮助你更好地利用 Shell 脚本解决实际问题,提高工作效率。

weixin_39372311的博客 1138

Linux 之shell脚本编程

01 Linux 系统环境 文件系统结构 / 虚拟目录的根目录。通常不会在这里存储文件 /bin 二进制目录,存放许多用户级的GNU工具 /boot 启动目录,存放启动文件 /dev 设备目录,Linux在这里创建设备节点 /etc 系统配置文件目录 /home 主目录,Linux在这里创建用户目录 /lib 库目录,存放系统和应用程序的库文件 /media 媒体目录,可移动媒体设备的常用挂载点 /root root用户的主目录 /sbin 系统二进制目录,存放许多GNU管理员级工

wangprince2017 6219

Linux之Shell脚本篇(一)

Shell编程是将Linux命令按逻辑存储在脚本文件中,通过/bin/bash等解释器执行,实现任务自动化以提高运维效率。脚本文件需以#!/bin/bash开头,添加执行权限后运行,示例包括打印"Hello World"和自动安装Nginx。Shell变量分为系统变量、环境变量和用户变量,用于简化数值调用和管理。系统变量如$0、$1等处理脚本参数,环境变量存储全局配置,用户变量为临时使用。开发规范包括变量命名规则、注释说明和逻辑顺序。进阶应用涉及条件判断(if语句)和参数验证,如Ngin

2401_83150173的博客 814

Linux之Shell脚本编程入门

Linux之Shell脚本编程入门,包括shell变量、运算符、条件判断、流程控制等语法案例

yuanjinshenglife的专栏 2341
上一篇: Ambari 安装确认主机 “SSLError“ 错误 终极解决方案
下一篇: Linux shell 脚本编程-高级篇 (二)
devalone
博客等级 码龄17年 108粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值