一次编写Shell脚本的过程

Posted DevinTest测试开发实践

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次编写Shell脚本的过程相关的知识,希望对你有一定的参考价值。

本文记录一次简单的编写Shell脚本的过程, 希望对新手略有启发帮助.

设计

使用工具: PlantUML

与其他语言一样, 在编写一个可执行程序/脚本前, 逻辑设计必不可少. 通常我的做法是, 在纸上简单用圈与线构思想法, 再用PlantUML画出具体的流程图.

UML脚本

以下是完整的uml脚本, 涉及到的几个关键词为: if else 判断, repeat 循环, fork 同级操作.

建议访问官网 http://plantuml.com/ , 下载pdf版学习更多PlantUML用法!

 
   
   
 
  1. @startuml

  2. start

  3. if (docker-compose installed?) then (yes)

  4.    repeat

  5.        :print selection;

  6.        fork

  7.            :init mongo;

  8.            :docker compose -f

  9.            init_mongo.yml up;

  10.        forkagain

  11.            :build web;

  12.            :print now ip;

  13.            if (need change ip?) then (yes)

  14.                :enter ip;

  15.            endif

  16.                :change config.js;

  17.            if (start build?) then (yes)

  18.                :docker compose -f

  19.                build_web.yml up;

  20.            else (no)

  21.                stop

  22.            endif

  23.        forkagain

  24.            :run xtest;

  25.        endfork

  26.    repeat while(exit?) is (no)

  27.        end

  28. else (no)

  29.    :print "Install first!";

  30.    stop

  31. endif

  32. @enduml

流程图

生成的流程图如下: 

编写Shell脚本

遇到的几个问题:

Q1: 如何判断当前环境是否装有某个工具?

A1: 以 docker-compose 为例, 将执行 which docker-compose 的结果返回给一个变量, 判断该变量为空则docker-compose未安装, 不为空则已安装. (具体参考函数 checkEnv)

Q2: 如何编写函数?

A2: 参考菜鸟教程: http://www.runoob.com/linux/linux-shell-func.html

除了编写函数, 处理参数的几个关键字也要了解, 其中用 $? 显示最后命令的退出状态, 在函数中可以用 return 1 表示执行失败, 用 return 0 表示执行成功, 然后在函数外使用 if 判断函数执行状态, 并作出相对应的操作.

Q3: 如何提取一个文件中的 IP ?

A3: 参考论坛5楼答案: http://bbs.chinaunix.net/thread-881629-1-1.html

该答案并非很严谨的实现对 IP 的提取, 实际过程中需要对数字的位数等做判断.

 
   
   
 
  1. cat config.js |grep -o -P "(\d+\.)(\d+\.)(\d+\.)\d+"

具体请查看函数 getIP

Q4: 如何修改某个文件的第一行为想要的内容?

A4: 修改文件通常使用的 sed 命令.

参考博客: http://blog.sina.com.cn/s/blog_68402d7b0102w794.html

使用 sed '1c Hi' ab 可以将第一行代替为Hi, 可以将字符串Hi替换为参数, 实现在读取到键盘输入后, 赋值给该参数, 再将文件第一行替换为该参数.

Q5: 如何读取键盘输入?

A5: read a 即将键盘回车前的输入赋值给 a 变量

Q6: 为何A4中的参数写入文件后为 $a 参数名, 而非a参数的值?

A6: 参考: https://www.jianshu.com/p/78522b72bdd8

在Shell中, 双引号与单引号有个重要的区别就是, 对变量的引用只能在双引号中, 因此需要用

 
   
   
 
  1. sed -i "1c $a" config.js

Q7: 如何编写选择功能?

A7: 使用 case 关键字完成.

Q8: 如何编写一个死循环?

A8: 参考: https://blog.csdn.net/askbai666888/article/details/8492348

 
   
   
 
  1. while :

  2. do

  3.     # do something

  4. done

" :" (空格+冒号)表示实现一个空语句,什么也不做,相当于C语言的";"

完整的Shell脚本

 
   
   
 
  1. #!/bin/bash

  2. function checkEnv(){

  3.    res=`which docker-compose`

  4.    if [ "$res"x != ""x ]; then

  5.        return 0

  6.    else

  7.        return 1

  8.    fi

  9. }

  10. # 1: init mongo

  11. function initMongo(){

  12.    echo "正在初始化MongoDB, 若失败或不小心退出请重试该步骤..."

  13.    sudo docker-compose -f init_mongo.yml up

  14.    echo "始化MongoDB完成!"

  15. }

  16. # 2: init node

  17. function getIP(){

  18.    ip=`cat config.js |grep -o -P "(\d+\.)(\d+\.)(\d+\.)\d+"`

  19.    echo $ip

  20. }

  21. function setIP(){

  22.    echo "是否需要重置 IP ? [y/n]"

  23.    read ans

  24.    if [ "$ans"x = "y"x ] || [ "$ans"x = "Y"x ]; then

  25.        read ip

  26.        setConfigJS $ip

  27.    elif [ "$ans"x = "n"x ] || [ "$ans"x = "N"x ]; then

  28.        echo "未修改 IP"

  29.        return 0

  30.    else

  31.        echo "输入有误"

  32.        return 1

  33.    fi

  34. }

  35. function setConfigJS(){

  36.    echo "IP 已修改为: (此处 IP 显示正确才可编译!)"

  37.    a="var apiHost = 'http://$1:8009/';"

  38.    sed -i "1c $a" config.js

  39.    getIP

  40. }

  41. function buildWeb(){

  42.    echo "正在编译前端代码..."

  43.    sudo docker-compose -f build_web.yml build --no-cache

  44.    sudo docker-compose -f build_web.yml up

  45. }

  46. function start(){

  47.    echo "正在启动 x-utest 测试平台..."

  48.    echo "启动完成后, 浏览器输入 IP:8099 访问."

  49.    sudo docker-compose up

  50. }

  51. function main(){

  52.    clear

  53.    echo "输入对应数字选择你需要的操作:"

  54.    echo " 1.初始化 MongoDB 2.编译前端代码 3.启动 x-utest 0.退出"

  55.    echo "注意: 执行步骤 3 前, 请先完成步骤 1, 2"

  56.    read select1

  57.    case "$select1" in

  58.        1)

  59.            initMongo

  60.            ;;

  61.        2)

  62.            echo "当前 IP 为: (此处 IP 显示正确才可编译!)"

  63.            getIP

  64.            setIP

  65.            if [ $? = 0 ]; then

  66.                echo "是否开始编译前端代码? [y/n]"

  67.                read ans2

  68.                if [ "$ans2"x = "y"x ] || [ "$ans2"x = "Y"x ]; then

  69.                    buildWeb

  70.                elif [ "$ans2"x = "n"x ] || [ "$ans2"x = "N"x ]; then

  71.                    echo "未编译, 正在返回主菜单..."

  72.                    return 1

  73.                else

  74.                    echo "输入有误, 正在返回主菜单..."

  75.                    return 1

  76.                fi

  77.            else

  78.                return $?

  79.            fi

  80.            ;;

  81.        3)

  82.            start

  83.            ;;

  84.        *)

  85.            echo "exit"

  86.            exit 1

  87.            ;;

  88.    esac

  89.    sleep 3;

  90. }

  91. checkEnv

  92. if [ $? = 0 ]; then

  93.    while :

  94.    do

  95.        main

  96.    done

  97. else

  98.    echo "请先安装 docker-compose!"

  99.    exit $?

  100. fi

点击“阅读原文”,简书版查看更方便!

以上是关于一次编写Shell脚本的过程的主要内容,如果未能解决你的问题,请参考以下文章

Linux Shell脚本编写——呈现数据

linux怎样一次运行多个shell脚本?

代码片段:Shell脚本实现重复执行和多进程

一次使用 Go 语言编写脚本的经历

记一次shell脚本编写及执行

如何编写幂等的shell脚本