Shell 函数
- Shell 函数调用和脚本执行方式一样, 但执行脚本时会为脚本单独创建一个新的Shell进程, 脚本结束后, 新创建的进程自动结束; 而调用函数时却不会, 是在原有进程中执行.
- 定义函数时需要使用圆括号, 调用函数时不需要圆括号
- 函数内部使用的变量, 默认都是全局变量. 除非使用local 关键字修饰
- 函数必须在使用前先定义
- 默认函数不能自定义返回值, 只能通过return来返回函数执行的状态, 但可通过echo 来间接返回数据
1. 函数
1.1 函数的定义
- Shell中函数定义可使用function 关键字修饰, 也可以不使用关键字修饰
- Shell中函数定义需要使用圆括号
- Shell中函数定义必须在函数调用之前
# 使用function 关键字
function sayHello(){
echo "hello,world"
}
# 不使用function 关键字
sayHi(){
echo "hi, $1"
}
1.2 函数的调用
- Shell 中调用函数, 直接使用函数名即可, 无须圆括号
- 函数调用必须在函数定义之后, 否则不能调用
# 无参调用
sayHello
# 有参调用
sayHi zhangsan
1.3 函数的返回值
- 默认情况下, Shell 中函数是没有返回值的. 但可以通过echo 来间接返回数据
# 定义求和函数
function sum(){
echo $(( $1 + $2 ))
}
# 通过$() 捕获命令输出
total=$( sum 1 2 )
echo "1+2=$total"
# 通过反引号捕获命令输出
total=` sum 1 2 `
echo "1+2=$total"
1.4 函数退出状态码
- 函数通过return 来返回函数退出状态码, 和exit 命令返回脚本退出状态码类似
- return 只能返回0-255 之间的整数, 0 表示函数正确执行, 非0 表示函数执行异常
- $? 用于获取程序上条命令的退出状态, 可紧接函数调用之后,用于获取函数退出状态
- 函数退出状态在编写jenkins 执行脚本时很有用,可以主动控制jenkins 任务的成功与失败
sayHello(){
echo "hello,world"
return 1
}
# 调用函数
sayHello
# 输出: status: 1
echo "status: $?"
# 输出: status:0
echo "staus: $?"
2. 函数参数
Shell 中函数获取参数的方式和Shell 脚本获取脚本参数的方式一致. 需要注意的是$0 即使在函数中也代表脚本名称, 而非函数名称.
2.1 位置参数
- 在使用$@ 或者 $* 时建议用引号包裹, 否则当参数出现空格时, 会出现意想不到的效果
- shift 表示从参数列表里弹出一个参数, 会影响$@, $#, $*的值
变量名称 | 描述 |
---|---|
$0 | 调用脚本时输入的脚本名称, 可能是脚本简单名称,也可能是脚本的全路径限定名 |
$n | 表示脚本名称后的第n个参数, n 的取值为[1,9],如:$1, $2… |
${n} | 表示脚本的第n个参数, n 的取值为[10,…],如:${10}, ${11}… |
$# | 脚本参数个数 |
$@ | 返回所有参数组成的数组, 遍历时需要用引号包裹 |
$* | 所有参数以空格拼接组成字符串, 遍历时需要用引号包裹 |
- 测试脚本:
#!/bin/bash
#Desc 函数位置参数测试
#Auth zongf
#Date 2018-10-30
test_param(){
echo "脚本名称: $0"
echo "参数个数: $#"
echo "所有参数: $*"
echo "所有参数数组: $@"
echo "第1个参数: $1"
echo "第10个参数: ${10}"
echo '********* 参数遍历$@ **********'
for arg in "$@"
do
echo $arg
done
echo '********* 参数遍历$* **********'
for arg in "$*"
do
echo $arg
done
echo '********* 弹出式遍历 **********'
while (( $# > 0 ))
do
echo "弹出参数:$#, 参数剩余:$# 个"
shift
done
}
# 调用函数
test_param 1 2 3 4 5 6 7 8 9 10 11 12
- 输出结果
脚本名称: ./test.sh
参数个数: 12
所有参数: 1 2 3 4 5 6 7 8 9 10 11 12
所有参数数组: 1 2 3 4 5 6 7 8 9 10 11 12
第1个参数: 1
第10个参数: 10
********* 参数遍历$@ **********
1
2
3
4
5
6
7
8
9
10
11
12
********* 参数遍历$* **********
1 2 3 4 5 6 7 8 9 10 11 12
********* 弹出式遍历 **********
弹出参数:12, 参数剩余:12 个
弹出参数:11, 参数剩余:11 个
弹出参数:10, 参数剩余:10 个
弹出参数:9, 参数剩余:9 个
弹出参数:8, 参数剩余:8 个
弹出参数:7, 参数剩余:7 个
弹出参数:6, 参数剩余:6 个
弹出参数:5, 参数剩余:5 个
弹出参数:4, 参数剩余:4 个
弹出参数:3, 参数剩余:3 个
弹出参数:2, 参数剩余:2 个
弹出参数:1, 参数剩余:1 个
2.2 选项参数
位置参数使用时,参数顺序必须固定,不能灵活处理可有可无的情况,所以便有了选项参数.
-
选项参数, 参数顺序可以不固定, 参数个数也可以不固定
-
选项参数和位置参数混合使用时,选项参数必须紧跟函数或脚本,处理完选项参数之后再处理位置参数.(否则处理会比较麻烦)
-
选项参数通过getopts 命令获取, 对于getopts 命令中的表达式, 如果选项有值,那么就跟冒号, 否则无须跟冒号
-
内置变量 $OPTARG 表示选项的值
-
内置遍历 O P T I N D 表 示 选 项 值 按 位 置 参 数 来 讲 是 第 几 个 参 数 , 在 OPTIND 表示选项值按位置参数来讲是第几个参数, 在 OPTIND表示选项值按位置参数来讲是第几个参数,在@参数数组中的索引为$OPTIND-1
-
测试脚本:
#!/bin/bash
#Desc 函数参数ops
#Auth zongf
#Date 2018-10-30
test_opts(){
# 获取选项参数
# 选项d 选项无值,所以无须冒号; 选择p,a,h 选项都有值,所以需要跟冒号
while getopts "dp:a:h:" arg
do
case "$arg" in
h)
echo "h:$OPTIND:$OPTARG"
;;
p)
echo "p:$OPTIND:$OPTARG"
;;
d)
echo "d:$OPTIND:$OPTARG"
;;
a)
echo "a:$OPTIND:$OPTARG"
;;
?)
echo "unkown argument."
exit 1
;;
esac
done
# 获取位置参数
local idx=$(( $OPTIND -1 ))
local args=${@:$idx}
local arg
for arg in $args
do
echo "参数;$arg"
done
}
# 调用函数
test_opts -h 127.0.0.1 -p 8380 -d -a hello world
# 测试错误选项
echo "******** 错误输出 ********"
test_opts hello world -h 127.0.0.1 -p 8380 -d hello world
- 脚本输出
test_opts -h 127.0.0.1 -p 8380 -d -a ded
h:3:127.0.0.1
p:5:8380
d:6:
a:8:hello
参数;hello
参数;world
******** 错误输出 ********
参数;-d
参数;hello
参数;world
3. 函数库
- 函数库和脚本基本类似, 但是通常函数库中只应该包含函数, 而不应该包括变量的定义.
3.2 Shell 函数库文件规范
- 库文件应该存放在同一的目录, 笔者存在Home目录的.libs/shell 目录中
- 库文件开发完成后, 推荐以.lib 结尾, 且权限为644, 不分配可执行权限
- 库文件中暴露的函数名命名规则为: 库文件名称.方法名; 内部函数命名规则为: 库文件名_函数名
- 库文件中函数中变量全部用local修饰, 防止和调用的变量冲突
- 库文件中推荐拥有help函数,输出函数的简单描述信息.
3.2 自定义日志库文件
- 编写日志库文件,打印消息时,输出日志级别和当前时间
- 库文件存放在 ~/.libs/shell/log.lib
#!/bin/bash
#Desc 日志库文件
#Auth zongf
#Date 2018-10-30
#Desc 帮助信息
log.help(){
echo "API List:"
echo "log.info param... 输出info 级别日志"
echo "log.warn param... 输出warn 级别日志"
echo "log.error param... 输出error 级别日志,会终端程序运行,exit 1"
}
######################### Private #########################
#Desc 打印日志
#Param1 日志级别
#Param2 日志内容
log_echo(){
local level=$1
# 弹出第一个参数
shift
local msg=$@
local time=`date +"%Y-%m-%d %H:%M:%S.%N"`
echo "[$level][${time:0:-6}] $msg"
}
######################### OPEN API #########################
#Desc 输出info级别日志
#Param1 日志内容
log.info(){
log_echo "info " $@
}
#Desc 输出warn级别日志
#Param1 日志内容
log.warn(){
log_echo "warn " $@
}
#Desc 输出error级别日志,终止程序运行
#Param1 日志内容
log.error(){
log_echo "error" $@
#终止程序运行
exit 1
}
3.3 引用自定义函数库
#!/bin/bash
#Desc 测试自定义函数库文件
# 引入自定义库, 使用绝对路径
. ~/.libs/shell/log.lib
# 调用函数
log.info hello wold
log.warn hello world
log.error hello wold