函数
和大多数编程语言一样,shell 脚本也支持函数。在 shell 可以用下面的方式定义和使用函数:
#!/bin/bash
# 定义函数
sayHello () {
echo "hello $1"
}
# 调用函数并传递参数
sayHello jack
上面的例子将输出“hello jack”。shell 中虽然也有函数的概念,但和 c、php、js 等编程语言中函数不太一样,shell 脚本中的函数更像是注入在主 shell 脚本中的一段子脚本,调用函数就像是运行这段子脚本,但它不是在新的进程中运行,所以可以和主 shell 脚本共享变量和函数。函数也可以像 shell 脚本那样通过 $0、$1 等变量获取参数。还要注意 shell 函数的调用一定要在函数定义之后,解释器是顺序解析的,在函数定义之前调用函数将报错。
另外,就像 shell 脚本通过 exit code 返回状态码那样,函数也可以通过 return code 的方式返回一个状态码。但要注意状态码不是文本数据,无法输出,想要返回文本数据可以在函数中 echo 文本数据,再通过反引号或 $() 的方式解析,例如:
#!/bin/bash
# 函数返回状态码
getCode() {
return 0
}
code=`getCode`
echo $code
# 输出为空
# 函数返回文本值
getData () {
echo "some text"
}
data=`getData`
echo "data is $data"
# 输出为 data is some text
最后 shell 函数也支持嵌套和递归调用,让我们可以实现一些复杂的算法逻辑,当然这样会比较吃资源。
#!/bin/bash
# shell 递归实现斐波那契数列
fibo () {
# 获取参数,即想输出数列的第几个值
index=$1
# index 为 1 或 2 时值为 1
# 用 -lt 选项进行数字小于比较,具体请参考 test 命令帮助
if [ $index -lt 3 ]; then
echo 1
else
# index 大于等于 3 时数列值为前两项的和
# 注意 shell 中变量都为字符串类型,要借助 expr 命令进行数字计算
n1=`expr $index - 1`
n2=`expr $index - 2`
expr `fibo n2` + `fibo n1`
fi
}
# 调用函数求斐波那契数列第 10 项的值
fibo 10
作用域
谈到函数当然逃不开作用域。在 shell 脚本中默认所有的变量都在全局作用域下,也就是全局变量。想要只在函数中有效的局部变量可以用 local 来声明。
#!/bin/bash
# 变量作用域
func () {
# 变量 val1 默认在全局作用域下
val1=123
# 用 local 声明变量 val2 为局部变量
local val2=456
echo "val1 in func is $val1"
echo "val2 in func is $val2"
}
func
echo "val1 outsite is $val1"
echo "val2 outsite is $val2"
# 运行结果
# val1 in func is 123
# val2 in func is 456
# val1 outside is 123
# val2 outside is
使用 getopts
getopts 是 shell 内建的一个命令,它可以用来检查和获取传递给命令的选项和参数。它的用法如下
#!/bin/bash
# getopts 获取选项和参数
# 脚本 getopts.sh
# getopts 的语法为 getopts opstring name
# opstring 中可以指定脚本接受哪些选项,如果选项必须提供参数就在后面加冒号
# 例如下面的 'xy:z:' 表示脚本接受 -x -y -z 选项且 -y -z 选项必须提供参数
# name 是一个 shell 变量用来保存选项
# getopts 还通过 $OPTIND 保存参数的索引,$OPTARG 保存参数的值
while getopts 'xy:z:' name; do
echo "$name" $OPTIND $OPTARG
done
# shell
./getopts.sh -xy "one" -z "two"
# 运行结果
# x 1
# y 3 one
# z 5 two
由于函数也可以看作是一段脚本,所以 getopts 命令也可以用在函数中来判断和获取函数接受的选项和参数。
函数库
如果我们有一些通用的函数或处理在很多地方都用的上,那么最好将他们做成库文件供其他脚本调用。类似 php 的 include,shell 脚本想要包含其他脚本或者说是库文件也很简单,用 . 操作符就行了,点后面跟脚本文件名就可以运行指定的脚本,它们不是另起进程而是在同一进程下运行,所以可以共享变量和函数定义,这样就实现了函数库的效果。
例如下面的代码将脚本 lib.sh 包含到当前脚本中,注意 . 操作符后面要有一个空格。另外也可以通过 source 来包含库文件,与 . 操作符效果相同。
#!/bin/bash
# 将指定的脚本文件包含到当前脚本
. /root/lib/lib.sh
# 用 source 命令可以达到相同的目的
source /root/lib/lib.sh
# 包含之后就可以使用 lib.sh 中定义的变量和函数了