介绍如何在 shell 脚本中创建函数,以及如何在 shell 脚本中使用这些函数
更多精彩
- 更多技术博客,请移步 IT人才终生实训与职业进阶平台 - 实训在线
导览
- 为了提高脚本中代码的重用性,可以将指定代码块封装为一个函数,然后在脚本中通过函数的名称对函数进行调用
- 脚本函数可以用函数最后一条命令的退出状态码或
return
命令来返回指定数值,范围在 0-255 之间 - 函数也可以使用
echo
语句输出返回值,这可以让函数的返回值不局限于数值 - 可以再函数中使用 shell 变量,函数的变量分为 全局变量 和 局部变量 ,局部变量 需要添加
local
前缀 - 函数可以调用包裹其自身在内的其他函数,函数调用自身的方式叫做 递归
- 在使用递归时必须设置一个终结条件,否则会导致无限循环
- 使用 函数库文件 可以实现函数在不同脚本中的调用,只需要在调用的脚本中使用
source
命令引入指定 函数库文件 即可 - 可以直接在命令行创建和调用函数,也可以将函数写在 .bashrc ,实现函数的全局调用
- 将 函数库文件 在 .bashrc 中进行
source
命令的调用,也可以实现函数的全局调用
17.1 基本的脚本函数
- 函数是一个脚本代码块,可以为其指定一个名称并在脚本中进行调用
17.1.1 创建函数
- 脚本中每个函数的名称都不能重复
17.1.1.1 基本语法
function name {
commands
}
17.1.1.2 简写语法
name() {
commands
}
17.1.2 使用函数
- 在脚本中创建函数后,就可以在后续代码中进行调用,如下图
- 需要注意的是,函数并不是必须得在脚本的开始位置进行创建 ,但是 函数必须在其被调用之前创建
-
如果函数的创建语句在其调用语句之后,shell 会因为找不到被调用的函数而抛出异常
17.1.2.1 函数名称的唯一性
-
如果在脚本中定义了重名的函数,那么新定义的函数会覆盖之前的函数,如下图
17.2 返回值
17.2.1 默认退出状态码
- 在 11.8.1 查看退出状态码 中已经介绍过退出状态码的概念,常见的退出状态码可以参考下图
- 默认情况下, 函数的退出状态码时函数中最后一条命令返回的退出状态码
- 在函数执行完毕后,可以立即用标准变量
$?
来确定函数的退出状态码,如下图- 可以看到,在
exitCode
函数的最后使用ls badDir
查询了一个不存在的目录 - 那么该命令的退出状态码肯定是 1 ,使用
$?
可以正常获取到该值
- 可以看到,在
- 但是如果有异常的命令不是最后一条命令,则无法得知该命令的执行结果,如下图
- 相对于上一个脚本,该脚本只是将函数内部的两个命令互换了位置
- 但是得到的函数返回值却截然相反,所以这种直接通过
$?
获取函数返回值的方式,没有多少实用意义
17.2.2 使用 return 命令
- 使用
return
命令可以退出函数,并返回特定的退出状态码 -
return
命令允许指定一个整数值来自定义函数的退出状态码,如下图
17.2.2.1 使用退出状态码作为函数返回值的缺陷
- 要获取退出状态码需要使用
$?
,而且需要在函数执行之后立即使用,如果在其中穿插了其他命令,就无法获取到函数最后一条命令的退出状态码 - 退出状态码的取值范围是 0-255 ,不在这个范围内的值无法作为退出状态码来赋予函数返回值
17.2.3 使用函数输出
- 函数可以将
echo
的输出内容作为函数的返回值,如下图- 函数中的
echo
语句虽然输出了一句话,但函数被调用后,这句话并没有立即输出 - 它作为函数的返回值被保存到了 result 变量中,直到该变量被
echo
使用时,函数的返回值才被输出 -
而且可以看到,使用这种方式时,函数的返回值不局限于数字
- 函数中的
17.3 在函数中使用变量
17.3.1 向函数传递参数
-
可以使用给脚本传参的方式给函数传参,函数内部对于传入参数的调用方式也和脚本中调用保持一致,如下图
17.3.2 在函数中处理变量
- 在函数中可以使用 全局变量 ,以及 局部变量
- 函数中定义的 局部变量 对于函数以外的部分,是不可见的
17.3.2.1 全局变量
- 默认情况下,在脚本中定义的任何变量都是全局变量
17.3.2.2 局部变量
- 使用
local
关键字声明的变量就是局部变量,该变量只能在其声明的函数内部才能访问,如下图- 两个 result 变量,虽然名称相同,但由于函数内部的是局部变量,所以互不影响
- 两个 result 变量,虽然名称相同,但由于函数内部的是局部变量,所以互不影响
17.4 数组变量和函数
17.4.1 向函数传递数组参数
- 如果试图直接将数组作为参数传递到函数中,将无法得到完整的数组内容,只能得到数组的第一个值,如下图
- 正常的使用
${array[*]}
可以获取到数组的全部内容 -
但是将数组作为一般参数传入到函数中后,不论如何操作,都只能获取到数组的第一个值,所以数组不能作为一般参数直接传入函数
- 正常的使用
17.4.1.1 将数组内容分解后传入函数
- 想要让函数能够获取到完整的数组内容,需要先将数据的内容分解成为普通的参数列表
- 然后在函数中对传入的所有参数进行重新组装,获取一个新的数组,如下图
- 将数组传入函数时使用的
arrayParam ${array[*]}
,其实就是将数组的输出结果1 2 3
作为普通参数全部传给函数- 完整形式就是
arrayParam 1 2 3
- 完整形式就是
- 然后在函数内容使用
($(echo "$@"))
将参数一次性输出,并包装成为数组内容
- 将数组传入函数时使用的
17.4.2 从函数返回数组
- 其实和将数组作为普通参数传入到函数中一样
-
想要从函数内部返回数组,只需要将函数内部的数组作为普通参数输出即可,如下图
17.5 函数递归
- 递归其实就是函数自己调用自己,但必须为递归准备一个最终条件,否则会导致无限循环
- 递归算法的经典例子是计算阶乘,例如
5! = 1 * 2 * 3 * 4 * 5 = 120
,公式是x! = (x * (x -1))!
-
用脚本来表示的话,如下图
17.6 创建库
- 利用 函数库文件 可以实现一个函数在不同的脚本中被重复调用
- 使用
source
命令可以在指定脚本中调用 函数库文件- 默认情况下,由于 shell 函数作用域的限制,函数只会在其被定义的脚本中生效
- 但是
source
命令会在当前 shell 上下文中执行命令,而不是创建一个新 shell ,相当于把目标脚本解析到了当前脚本中
-
source
命令的语法分两种- 完整语法
source ./file
- 简写语法
. ./file
- 完整语法
-
写一个简单例子演示一下,如下图
17.7 在命令行上使用函数
17.7.1 在命令行上创建函数
- 使用这种方式创建的函数都是临时的,一旦当前 shell 退出,这些被创建的函数都将不复存在
- 而且这种方式不但没有什么实际意义,还非常危险,如果误将函数的名称和当前系统中其他命令的名称重名了,那么这个系统命令将会被覆盖
17.7.1.1 单行定义函数
- 其实就是将一个简单的的函数直接在命令行上进行创建,如下图
-
在每个命令的最后都需要添加分号,这样 shell 才会知道命令的开始和结束在什么时候发生
-
17.7.1.2 多行定义函数
- 使用 shell 的 次提示符( > ) 来输入更多命令,如下图
- 当输入
{
后按下回车,会直接进入函数的多行模式 - 当输入
}
后按下回车,会直接退出函数的多行模式
- 当输入
17.7.2 在 .bashrc 文件中定义函数
- 因为 .bashrc 文件是的特性,用户在开启每个 shell 进程时,系统都会扫描该文件
- 所以可以把该文件作为一个默认的 全局函数库文件 ,将函数直接写在这个文件中,将可以直接实现函数的全局调用
- 但该文件中也会存在一些系统预置的内容,所以直接添加各种自定义函数,容易导致文件内容变的混乱
- 所以也可以使用
source
命令将一些自定义的 函数库文件 ,在该文件的末尾进行引用
17.8 实例
- 介绍 GNU shtool shell 脚本函数库如何安装和使用,跳过了
网友评论