Shell脚本编程——函数

380 阅读3分钟

前置知识要求

  • Linux 命令行基础
  • Shell 编程基础概念

如何创建一个函数

方式一:

#!/usr/bin/env bash

function myFunc {
    commands
}

方式二:

#!/usr/bin/env bash

myFunc () {
    commands
}

myFunc 是函数的名字,脚本中定义的每个函数都必须有一个唯一的名称。 如果有多个同名的函数,那么后面的定义就会覆盖前面的,更重要的是这一切不会产生任何错误!!!

如何调用一个函数

#!/usr/bin/env bash

function myFunc {
    echo "This is our first function demo."
}

# 直接使用函数名即可,就像调用其他 shell 命令一样
myFunc

image.png

必须在函数被定义之后,才可以使用这个函数,不然会报错“command not found”

如何在函数中定义局部变量

默认情况下,在脚本中定义的变量都是全部变量。在函数外定义的变量都可在函数内部随意使用。同样地,在函数内部定义的全局变量,也可以在脚本其他地方读写。

这样太混乱且不安全,所以我们需要一种方式能够在函数内部定义局部变量。这个方式就是 bash shell 提供的 local 命令。

#!/usr/bin/env bash

top="top"

function testLocalVar {
    # global variable
    ridge="ridge"
    # local variable
    local regional="regional"

    echo "function interior, access global variable 'top': $top"
    echo "function interior, access local variable 'regional': $regional"
}

testLocalVar

echo
echo "script top, access global variable 'ridge' defined in function: $ridge"
echo "script top, access function interior local variable 'regional': $regional"

image.png

如何向函数传参

bash shell 会将函数当作小型脚本来对待。所以我们可以像普通脚本那样向函数传递参数。 也就是说,函数可以使用标准的环境变量参数来表示传给函数的参数:

  • $0:函数名字
  • $1:函数的第1个实参
  • $2:函数的第2个实参
  • $#:函数的实参个数
  • $*:函数的所有参数当作一个单词保存(将这些参数视为一个整体而不是多个独立的个体)
  • $@:函数的所有参数当作同一字符串中的多个独立的单词
#!/usr/bin/env bash

function printName {
    echo "Hello $1"
}

user="juejin"
# 调用时实参必须和函数名字放在同一行:
printName $user

image.png

如何向函数传递脚本命令行中的参数值

#!/usr/bin/env bash

function printCommandLineParams {
    echo "the first parm from command line input is: $1"
}

# 手动地将脚本中的特殊环境变量参数传递给函数即可
printName $1

如何向函数传递数组呢

很遗憾,在 Bash 中不能将数组变量当作单个参数传递给函数;要想实现这一点,我们只能是将数组展开,然后在函数内部再依此创建一个新的数组。

#!/usr/bin/env bash

function printArray {
    newArr=("$@")
    echo "${newArr[*]}"
}

aArr=(1 2 3 4 5)
printArray "${aArr[*]}"

image.png

如何从函数中返回值

函数的退出状态码

bash shell 会把函数当作一个小型脚本,运行结束时会返回一个状态码。默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以使用标准变量 $? 来确定函数的退出状态码。

#!/usr/bin/env bash

function testStatusCode {
    ls -l non-existent
    echo "trying to display a non-existent file"
}

testStatusCode
echo "The exit code status is: $?"

image.png

上面代码,函数中最后一条命令执行成功,所以函数的退出状态码是 0,即使函数内部有执行出错的命令。

return 命令

bash shell 使用 return 命令来退出函数并返回特定的退出状态码。return 命令允许指定一个整数值来定义函数的退出状态码。

#!/usr/bin/env bash

function customStatusCode {
    return $(( value * 2 ))
}

value=3
x=$(customStatusCode $value)

echo "The new value is $?"
echo "The x is $x"

image.png

可以看到,return 命令自定义的状态码只能通过 $? 参数来获取,而且也没法向外输出值。 而且使用这种方式获取值,还有 2 个问题:

  • 必须在函数一结束就获取 $? 的值
  • return 命令的值必须是 0~255

如果在使用 $? 变量值之前执行了其他命令,那么 return 命令的值就会丢失。而如果 return 的值不在[0, 255] 之间,会产生一个错误值。

echo 输出

为了将函数内部的数据值向外部输出,我们可以在函数内部的最后使用 echo 命令,然后在外部使用变量保存函数输出的这个值即可:

#!/usr/bin/env bash

function add {
    echo $(( $1 + $2 ))
}
first=1
second=2
sum=$(add $first $second)
echo "the sum of $first and $second is: $sum"

image.png

这种方式也是有问题的,如果函数内部有多个 echo

#!/usr/bin/env bash

function add {
    echo "Hi~I'm add function"
    echo $(( $1 + $2 ))
}
first=1
second=2
sum=$(add $first $second)
echo "the sum of $first and $second is: $sum"

image.png

echo 输出数组

正如同向函数传递数组参数那样,在 Shell 脚本中,没法将一个数组变量作为单个变量值输出。能做的只是在函数内部按照正确顺序输出数组中的每个值,然后在函数外部将这些值重新组装为一个数组。

#!/usr/bin/env bash

function outputArr {
    local originalArr=("$@")
    local newArr=("$@")
    local len=$#
    local i=0

    while [ "$i" -lt "$len" ]
    do
        newArr[$i]=$(( ${originalArr[$i]} * 2 ))
        i=$(( i + 1 ))
    done

    echo "${newArr[*]}"
}

myArr=(1 2 3)
result=($(outputArr ${myArr[*]}))

echo "The new Arr is : ${result[*]}"

image.png

如何实现递归

这是一个 Bash Script 实现的求阶乘函数:

#!/usr/bin/env bash

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

如何使用另一个脚本中的函数

使用 source 命令,我们可以在当前 Shell 上下文中执行命令,而不是创建一个新的 shell。

#!/usr/bin/env bash

# 假设 utils.sh 文件与当前脚本在同一目录下
source ./utils.sh
# 在上面这行命令之后,我们接下来便可以使用 utils.sh 中定义的函数了

source 命令的别名

source命令有个快捷的别名,称作点操作符,之前的例子可以改写为:

#!/usr/bin/env bash

# 假设 utils.sh 文件与当前脚本在同一目录下
. ./utils.sh
# 在上面这行命令之后,我们接下来便可以使用 utils.sh 中定义的函数了

参考

  • Richard Blum & Christine Bresnahan.《Linux命令行与shell脚本编程大全(第3版)》.北京:人民邮电出版社.2016