系统相关
首页 > 系统相关> > 写shell必备辅助神器ShellCheck

写shell必备辅助神器ShellCheck

作者:互联网

ShellCheck 的目标是

以上来自于官网github文档的谷歌机翻,地址 https://github.com/koalaman/shellcheck

我遇到了什么shell问题?

我直接贴一下当时用的代码吧。

echoError() { echo -e "\033[31m$@\033[0m" }      # red
echoWarning() { echo -e "\033[33m$@\033[0m" }      # yellow
echoSuccess() { echo -e "\033[32m$@\033[0m" }      # green

echoInfo "\n---------------------检查是否正确挂载/database盘---------------------------"
database_mount=$(df -hT | awk '$7~/^\/database$/{print $7}')
if [[ "database_mount" == "/database" ]];then
        echoSuccess "找到独立挂载的 /database"
        #找到/database盘后打印盘的文件系统类型
        filesystem_type=$(df -hT | awk '$7~/^\/database$/{print $2}')
        if [[ "filesystem_type" == "xfs" ]];then
                echoSuccess "文件系统格式正确,为 xfs"
        else 
                echoError "文件系统格式错误,为 ${filesystem_type}"
        fi
        lsblk |awk '{print $6 $7}' |grep 'lvm/database$' >/dev/null \
        && echoSuccess "/database 采用 lvm 逻辑卷管理,这是建议的,方便扩容" \
        || echoWarning "/database 没有采用 lvm 逻辑卷管理,不方便扩容,请确认"
else
        echoError "未找到独立挂载的 /database"
fi

代码一开始,先定义 3 个颜色输出函数,红色为 error 级别,黄色为 warning 级别,绿色为 success 级别。

我的代码要实现的功能是,检查服务器磁盘数据目录 /database 的挂载情况,如果 /database 目录未独立挂载则输出 error,如果 /database 目录独立挂载,则继续判断是否是 xfs 文件系统格式挂载,不是的话则输出 error,再判断是否适用了 lvm 逻辑卷管理,不是的话则输出 warning,其余情况输出 success。

执行脚本结果如下:

[root@192-168-199-132 ~]# sh -x test.sh 
test.sh: line 22: syntax error: unexpected end of file

报了个语法错误,但我对这个报错一脸懵逼,第 22 行有语法错误?我的脚本压根没有 22 行好吗?(最后一行是 21 行)

我仔细又读了一遍代码,还是没有找到原因,这时,我怀疑是不是因为我脚本是 windows 平台上编写,传上去后有特殊字符导致的?

小技巧: cat -A

注意了,必须 cat -A 才能看得到肉眼看到换行符、制表符,直接 vi 或 vim 是分辨不出来的

图片

Unix 系统的换行符为 \n,而 Windows 的换行符是  \r\n,把 Windows 记事本编辑的文件上传到 Unix 系统后,就有可能有换行符兼容问题,在每行末尾多显示个 "^M"

如图,test.sh 脚本没有 windows 的特殊换行符"^M",因为我当时是用 notepad++ 编写的,选择了 Unix 模式。

作为对比,下面这个是 windows 上用记事本开发的 test2.sh 脚本,带 windows 的特殊换行符"^M"

图片

"^M"也可以理解为 windows 的特殊换行符了,^I 表示 tab 制表符(倒数第二行,蓝色方框,为了演示我这里故意偷偷替换了一处空格为制表符)。

在 shell 中制表符不影响运行,而 ansible playbook 则影响运行。而 "^M"则对 shell 或 ansible playbook 都影响。

所以,带 "^M" 的 test2.sh 脚本运行的报的是另外一种错误。

[root@192-168-199-132 ~]# sh -x test2.sh 
test2.sh: line 13: syntax error near unexpected token `else'
'est2.sh: line 13: `        else 

我们可以使用 dos2unix 命令很方便的把 windows 换行符转换为 unix 换行符

[root@192-168-199-132 ~]# dos2unix test2.sh 
dos2unix: converting file test2.sh to Unix format ...
[root@192-168-199-132 ~]# sh -x test2.sh 
test2.sh: line 22: syntax error: unexpected end of file

现在 test2.sh 执行报错和 test.sh 一样了。

ShellCheck登场

为了解决这个奇怪的语法报错问题,我只好下载了知名工具 ShellCheck,我给这个工具的定位是,语法检查 + 代码质量检查工具。

如何安装 ShellCheck?

和大多数软件的安装方法一样,也是这三种方法:

apt法:

# apt 安装 shellcheck
sudo apt install shellcheck
shellcheck --version  #version 0.3.8

二进制法

wget https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.linux.x86_64.tar.xz
tar Jxvf shellcheck-v0.8.0.linux.x86_64.tar.xz
cd shellcheck-v0.8.0
./shellcheck --version  #version 0.8.0

没必要源码安装,二进制安装快,并且版本最新,建议用二进制方法安装。

水落石出

(图片建议点开放大查看)

图片

通过 shellcheck,我发现 line 22 这个报错,和 "}" 有关,但不知道哪个 "}",先不管他。

然后 line1、line2、line3 的 SC1083 报错是一样的,说我可能少了个";" 号。

确实,这里是需要分号的,用 vim 可以证明。

图片

这里可以看出,echoError 后面的 "{" 是天蓝色的,而最后的 "}" 居然是红色的,也就是 vim 不认为他们是一组括号的关系。这个 vim 的语法高亮是可以看出来的,但 notepad++ 的语法高亮功能看不出!

Notepad++ 截图:

图片

添加 ";" 号修正后,括号颜色正常了。

图片

我们继续调试,有趣的是,line1、line2、line3 冒出来新的报错。

图片

报错的编号是SC2145,有红色的简单的文字说明,而最底下有个链接,可以去官网上看详细的原因说明。

其实我这里用 "或者*" 效果是一样的。

但 shellcheck 误以为我用了数组,如下图:

图片

行吧,既然等价,我就按他的提示修改为  "$*" 吧。

修改后再执行 shelllcheck 如图:

图片

我依然优先处理红字部分,这些都是严重问题,是脚本认为你脚本有问题的部分,黄色是警告,是脚本怀疑你有错的部分,而绿色是脚本提醒你用法不太规范提醒你可能会踩坑。

这里的红色部分发生在 line 1 的头部,也就是 line 1 前的上一行,文字也提醒我了,要添加 shebang,也就是 "#!/bin/bash"

添加完 shebang 后,我们开始处理黄色的部分。

图片

黄色的部分提醒我,database_mount 变量没有使用到,提醒我 "database_mount" == "/database" 和 "filesystem_type" == "xfs" 两个静态值做比较,是不是其实搞错了,应该是有一个是变量?

确实是这样的,于是我修改了这两行代码

if [[ "database_mount" == "/database" ]];then 
改为
if [[ "${database_mount}" == "/database" ]];then

if [[ "filesystem_type" == "xfs" ]];then
改为
if [[ "${filesystem_type}" == "xfs" ]];then

这个提醒非常之好,shell 脚本绝对不会提醒你,因为语法没有错误,shell 是脚本测试时才能发现有逻辑错误,我们在测试之前就提前发现问题了,非常棒!

图片

最后,我只剩下这个问题要修改了。shellcheck 的提示是:

Note that A && B || C is not if-then-else. C may run when A is true.

这个很好理解,他是建议我把 A && B || C 的语法修改为 if-then-else 形式,因为他们不是等价的。

我举个例子:

ls && echo ok || echo notok

我这句命令的意思是,执行 ls 成功的话,就会执行 echo ok,在屏幕打印 ok,否则 echo notok,在屏幕打印 notok

图片图片

一般情况他是运作良好的。但假设,我这里代码(黄底字)出错了,则会有意料之外的输出结果

ls && echook || echo notok

这里,我 echo 和 ok 之间忘记添加空格了,导致这个逻辑段执行失败。

图片

这样的话,虽然执行 ls 成功了,他也能执行echook逻辑,但由于echook 逻辑有问题,最后他还走了第二个逻辑,我们称为"否则"的逻辑,输出了 not ok,这不是我们要的,因为 ls 实际上是执行成功的。

如果我们改为 if-then-else 形式,则没有这个问题。

图片

走了 echook 逻辑,虽然这个逻辑有问题,但这就结束了。虽然没有成功 echo ok,但至少不会走"否则"那个逻辑。

        lsblk |awk '{print $6 $7}' |grep 'lvm/database$' >/dev/null \
        && echoSuccess "/database 采用 lvm 逻辑卷管理,这是建议的,方便扩容" \
        || echoWarning "/database 没有采用 lvm 逻辑卷管理,不方便扩容,请确认"

由于我能确保我的第一个逻辑(黄底字)语法正确,并且我的否则逻辑只是输出告警文本,而不是执行一些变更性的命令,影响不大,我不打算修改调整了。但我确实从 shellcheck 学会和记住了这个写 shell 时要注意的坑。

最后给大家看看修改前后的对比

图片

Enjoy Shell!

标签:逻辑,shell,database,shellcheck,echo,神器,sh,ShellCheck
来源: https://www.cnblogs.com/sll120/p/16607010.html