10. Shell-函数

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 函数库文件规范

  1. 库文件应该存放在同一的目录, 笔者存在Home目录的.libs/shell 目录中
  2. 库文件开发完成后, 推荐以.lib 结尾, 且权限为644, 不分配可执行权限
  3. 库文件中暴露的函数名命名规则为: 库文件名称.方法名; 内部函数命名规则为: 库文件名_函数名
  4. 库文件中函数中变量全部用local修饰, 防止和调用的变量冲突
  5. 库文件中推荐拥有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

你可能感兴趣的:(shell)