本文共 4536 字,大约阅读时间需要 15 分钟。
最近在公司使用Jenkins自动化编译前端、Android、iOS时遇到了挺多的shell脚本的坑,以前都是从网上找一些脚本改改测试可用就直接用了,但是最近项目变化大,导致自动化编译总是出错,于是决定好好学习下shell脚本如何正确的编写!以下是我个人的实际项目所用的一些总结,我大致会围绕三个问题来聊聊我遇到的坑和解决方法:
由于本人只是一个前端开发者,对于Linux的shell脚本还处于一脸懵逼状态,可能我的方法和您的不一致,如果您感觉我的方法和思路有什么不对的地方还请大神给予指正~
首先将可用代码贴出来:
#获取上次提交和本次提交的差异git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT > ${WORKSPACE}/${APP_ID}commint.logrowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")# 文件更新数 小于等于 0 无需构建if [ $rowNum -le 0 ];then echo "代码没有任何修改,项目无需构建" exit 0fi
git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT > ${WORKSPACE}/${APP_ID}commint.log
这句话实现了两个功能:
git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT
> ${WORKSPACE}/${APP_ID}commint.log
查看git两次提交的差异
我们知道git diff 可以得到文件的差异,但是这里我们只需要得到差异的文件名称就可以,根据得到 --name-only
参数可得到文件名称,那么 $GIT_PREVIOUS_SUCCESSFUL_COMMIT 和 $GIT_COMMIT
是什么呢?
根据Jenkins官方文档中的可用的shell变量一文得知
OK,得到两次提交的差异文件列表之后为方便后续使用,我们可以利用shell 中的 >
重定向功能把结果输出到一个文件中!
>
将前一个命令的标准输出结果 以文本替换的方式 重写
进一个文件中,当文件不存在时自动创建该文件>>
将前一个命令的标准输出以 追加
的形式,追加进一个文件的末尾行,当文件不存在时自动创建该文件最终利用 git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT > ${WORKSPACE}/${APP_ID}commint.log
句话我们得到了git上次提交跟本次提交的差异文件,并将结果输出到了当前Jenkins任务的工作空间根目录,文件名为commit.log里!
rowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")
我们已经得到了差异的文件信息并写入到了commit.log文件里,那么只需要读取这个文件,统计下这个文件里有多少行,是不是就可以得到本次修改了多少个文件?
根据《Linux+shell脚本攻略(第二版)》一书第四章 让文本飞 一文中得知 使用awk 可以统计文件的行数!
awk简介
awk特殊变量
使用awk统计文件中的行数
所以我们得到了这条统计文件内容行数的命令,并将改命令的结果赋予rowNum
变量
$ rowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")
根据《Linux命令行于shell编程大全(第三版)》中 第12章 使用结构化命令 一文中得知:
使用结构化语句
数值比较
if [ $rowNum -le 0 ];then echo "代码没有任何修改,项目无需构建" exit 0fi
在使用shell脚本自动化编译的时候经常会遇到当某个命令不存在或者没有安装的时候直接报错,终止了编译!现在解决的就是当遇到命令找不到的时候直接安装该命令
先把最终实现代码贴出来然后一点点去分析(拿移动端代码热更新举个例子):
#检测有没有code-push-cli,没有直接全局安装if hash code-push 2>/dev/null; then echo "有code-push-cli"else npm install code-push-cli@latest -gfi
参考自 方法大致有三种:
command -vhash # For regular commands. Or...type # To check built-ins and keywords
以上三种方式随意一个就可以了,看个人爱好了!现在来看下 2>/dev/null
的作用,在开始之前首先需要简单说下什么是文件操作符
2.1 文件描述符
当执行shell命令时,每个 Unix/Linux 命令运行时会默认打开3个文件,每个文件有对应的文件描述符来方便我们使用:
类型 | 文件描述符 | 默认情况 | 对应文件句柄位置 |
---|---|---|---|
标准输入(standard input) | 0 | 从键盘获得输入 | /proc/self/fd/0 |
标准输出(standard output | 1 | 输出到屏幕(即控制台) | /proc/self/fd/1 |
错误输出(error output) | 2 | 输出到屏幕(即控制台) | /proc/self/fd/2 |
所以我们平时在执行shell命令中,都默认是从键盘获得输入,并且将结果输出到控制台上。但是我们可以通过更改文件描述符默认的指向,从而实现输入输出的重定向。比如我们将1指向文件,那么标准的输出就会输出到文件中。
2.2 输出重定向
输出重定向的使用方式很简单,基本的一些命令如下:
命令 | 介绍 |
---|---|
command >filename | 把标准输出重定向到新文件中 |
command 1>filename | 同上 |
command >>filename | 把标准输出追加到文件中 |
command 1>>filename | 同上 |
command 2>filename | 把标准错误重定向到新文件中 |
command 2>>filename | 把标准错误追加到新文件中 |
2.3 >/dev/null
/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。
2.4 2>&1
这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是错误输出将和标准输出同用一个文件描述符,说人话就是错误输出将会和标准输出输出到同一个地方。
2.5 >/dev/null 2>&1
linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令,所以>/dev/null 2>&1的作用就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。
参考自
小结
hash code-push 2>/dev/null
用hash
来判断code-push
命令是否存在,并且把标准错误重定向到Linux黑洞中,以保证脚本的完整运行
拿移动端代码热更新 code-push
举个例子,根据code-push whoami
的执行结果判断当前code-push是否登录,未登录执行登录操作
先贴下代码:
#登录code-push私服 WHOAMI=`code-push whoami 2>&1`if echo "$WHOAMI" | grep "userName" >/dev/null; then echo "code-push login Successfully"else code-push login fi
经过上面对文件操作符和重定向的学习,在看如上这段代码是不是好理解多了?
WHOAMI=`code-push whoami 2>&1`
正常来说 code-push whoami
输出的是您当前登录的用户名,如果当前处于未登录状态,那么该命令会使用文件操作符2 输出标准的错误信息,那么我们用到 2>&1 重定向绑定语句将标准错误输出和标准输出信息保存在WHOAMI变量中
echo "$WHOAMI" | grep "userName" >/dev/null
该语句的关键在于 Linux 的管道操作符 和 grep 过滤器
>/dev/null
将左侧所有的输出重定向到Linux黑洞
我个人的思路就是捕获一切可能返回状态码为2的错误输出!保证程序能够按照预想的正常运行!欢迎拍砖~
转载地址:http://asqni.baihongyu.com/