Shell编程从入门到实践——实践篇

欢迎关注 「Android茶话会」

  1. 「学习之路」 取Android技术路线经典电子书
  2. 「pdf」 取阿里&字节经典面试题、Android、算法、Java等系列武功秘籍。
  3. 「天涯」 取天涯论坛200+精彩博文,包括小说、玄学等

背景

之前在搞一些CI/CD,使用到了shell脚本,shell的开箱即用确实比较方便,至少无需在宿主上安装运行环境,本篇文章主要解释shell脚本实践过程中一些经验总结。

实践篇

模块化

刚开始看一些之前的shell脚本,一个脚本大几百行,很少有函数的情况,其实shell脚本也可以函数化,按照模块的拆分,这样就会带来良好的可读性和可维护性,通常我们会先定义main函数,将功能分解为一个个子函数

  • 模块化之前
    Shell编程从入门到实践——实践篇_第1张图片
  • 模块化之后
    Shell编程从入门到实践——实践篇_第2张图片
#!/bin/bash

localvar="fun1"

main() {
    func1
    func2
}

func1() {
    local localvar="funlocal"
    echo ${localvar}
    localvar="fun2"
}

func2() {
    echo ${localvar}
}

main "$@"

函数

函数是模块化的基础,一个函数往往负责一件事件

  • 函数名后面的圆括号不加任何参数
  • 函数的完整定义必须置于函数的调用之前
函数名 (){
	函数体
}

传参

#!/bin/bash

print_something(){
    echo "hello $1" # $1 获取第一个参数
}
print_something Lion # Lion 为参数
print_something Frank # Frank 为参数
$1~$9:函数的第一个到第9个的参数。
$0:函数所在的脚本名。
$#:函数的参数总数。
$@:函数的全部参数,参数之间使用空格分隔。
$*:函数的全部参数,参数之间使用变量$IFS值的第一个字符分隔,默认为空格,但是可以自定义。
$?:显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。可以用于函数返回值

返回值

testFun(){
    echo "helloworld!"
    return 99
}

# 千万要注意shell并不像其他语言直接返回返回值,其返回值放到$?中,这也是为什么只能返回整型的原因
# 所以这种承接方法是错误的,获取到的值是echo打印的内容
# return_value=`testFun`
# 以下才是正确获取通过return返回的返回值的正确写法
testFun
echo "the return value is: $?"

局部变量

  • 不做特殊声明,shell中变量都是全局变量
  • 局部变量 使用 local 关键字,函数内外同时存在同名变量,则函数内部会覆盖函数外部变量

脚本之间引用

模块化之后多个脚本和公共参数之间是可以相互复用的 这时候可以通过 souce或者点号来调用所需要的脚本

source ./util.sh
. ./util.sh

错误处理

如果什么都不做,在shell中命令出错也不影响,默认会继续执行,这会带来麻烦,有时候我们需要区分业务错误和系统错误,比如在脚本执行遇到系统错误之后就应该退出,遇到业务错误,需要根据业务错误来确定是否往下执行,有以下几种方式来控制shell的错误

set 命令

  • set -e

只要脚本发生错误就终止执行,set +e表示关闭-e选项,set -e表示重新打开-e选项,但是要注意这个命令不适与管道操作

set +e
command1
command2
set -e

管道处理需要借助

  • set -o pipeline

通常我们会把这些命令放在一起使用

# 写法一
set -Eeuxo pipefail

# 写法二
set -Eeux
set -o pipefail

短路符号

如果command正常退出,返回0,|| 运算符右半部分被短路,脚本继续执行。
如果command异常退出,返回非0, 运算符右半部分执行,脚本exit 1。

command || exit 1

# 写法一
command || { echo "command failed"; exit 1; }

# 写法二
if ! command; then echo "command failed"; exit 1; fi

# 写法三
command
if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi

使用trap 捕获信号量

用来在bash脚本中响应系统信号,trap命令必须放在脚本的开头。否则,它上方的任何命令导致脚本退出,都不会被它捕获。标准格式

$ trap [动作] [信号1] [信号2] ...

HUP:编号1,脚本与所在的终端脱离联系。
INT:编号2,用户按下 Ctrl + C,意图让脚本终止运行。
QUIT:编号3,用户按下 Ctrl + 斜杠,意图退出脚本。
KILL:编号9,该信号用于杀死进程。
TERM:编号15,这是kill命令发出的默认信号。
EXIT:编号0,这不是系统信号,而是 Bash 脚本特有的信号,不管什么情况,只要退出脚本就会产生。

$ trap 'rm -f "$TMPFILE"' EXIT

表示 脚本遇到EXIT信号时,就会执行rm -f “$TMPFILE”

调试

也没有特别好的办法,可以让不同级别的日志打印出不同的颜色

function debug()
{
    echo -e "\033[37m$1\033[0m"
}
function infolog()
{
    echo -e "\033[32m$1\033[0m"
}
function warn()
{
    echo -e "\033[33m$1\033[0m"
}
function error()
{
    echo -e "\033[31m$1\033[0m"
}

其他细节

预定义默认值

  • ${varname:-word} varname存在且不为空,则返回它的值,否则返回word
  • ${varname:=word} varname存在且不为空,则返回它的值,否则将它设置为word并返回word
  • ${varname:+word} varname存在且不为空,在返回word,否则返回空值,它的目的是测试变量是否存在
  • ${varname:?message} 如果变量varname存在且不为空,则返回它的值,否则打印出varname: message,并中断脚本的执行,它的目的是防止变量未定义

( ) 与 ()与 (){}的区别

前者用于命令执行,返回命令返回值

all_files=`ls` # 获取ls命令的执行结果
all_files=$(ls) # 效果同上

后者用于变量展开

echo ${A}B

[]和[[ ]] 、(())

在使用[]或者test指令进行字符串判空时,需要在引用的变量上加上双引号""。
如果使用[[]]的话就不需要。
$(())用来做整数运算的

curl中携带参数

curl中需要用单引号,数字和字符还不一样,注意tesMsg

jobId="78707463"
tesMsg="msg:需要找专人审批"

curl -X POST https://xxxx/openapi/xxxx/job/update_msg -H "Content-Type: application/json" -d '{
"jobId":'$jobId',
"jobMsg":"'"${tesMsg}"'"
}'
  1. 「学习之路」 取Android技术路线经典电子书
  2. 「pdf」 取阿里&字节经典面试题、Android、算法、Java等系列武功秘籍。
  3. 「天涯」 取天涯论坛200+精彩博文,包括小说、玄学等

您的 点赞、评论、转发 是对我的巨大鼓励!

你可能感兴趣的:(android,shell)