shell脚本写作技巧与艺术
shell的江湖地位
社会的个体是人,互联网的个体是服务器,有人的地方就有江湖,那么服务器们也形成了江湖,这个江湖很有趣,总是两眼一抹黑,黑漆漆的屏幕,闪烁的光标不停的发出骇人的射线,把心志不坚定的人们赶出去。这导致存在即高手。
东道主
服务器领域中,shell脚本语言无疑是作为任何服务器编程语言的东道主的。所有的编程语言(确切的说是服务端编程语言)都需要使用一定的命令才能形成自己的服务,比如java、javaw、python、npm(nodejs)、gcc等等,他们都是对应编程语言的命令,借助这些命令才能形成程序。任何可以使用的命令,都是shell脚本可以容纳的。shell就是各个编程语言的大管家。
老祖
shell语言诞生的非常之早。可以说第一台具备操作系统的计算机出现的时候,shell就对应存在了(当然,这个shell语言不是Linux shell)。之所以新人会感觉害怕,就是因为这个语言没有良好的人机交互,总给人一种,万一弄错系统就崩溃了。这也反面说明了,越懂操作系统,使用shell就越顺手。正因为如此,江湖有人说:“windows操作系统入门简单,精通难;Linux操作系统入门难,精通容易”。毕竟玩Linux就是直接拿shell开始玩,能玩懂了,这个系统也就懂了。
外星人
如果编程语言是人,shell就是外星人。shell语言灵活,充满个性,想要读懂很难,想要理解其深刻艺术性更难。笔者曾经三次查看MySQL的官方安装文档,每次查看大概间隔一年多左右,随着对Linux的理解加深,每次看得到的收获不一样。shell语言没有捷径,唯有更懂操作系统,多看别人的脚本,不断提升。
shell语言没有捷径,唯有更懂操作系统,多看别人的脚本,不断提升。这句话让你想到了什么?
shell之痒
shell很容易成为孤儿,应该熟练使用它的运维工程师觉得开发脚本是研发工程师的责任,研发工程师觉得自己只是在windows上开发程序而已,和服务器操作系统(通常是Linux的某个发行版)没有一毛钱关系,脚本应该是伺候服务器的运维来写的。总之,到了最后,没人写。
无效的编程思想
shell的函数有调用顺序
shell是一行一行读取执行的,这和JavaScript语言不一样。由于没有整个文件被读取,没有形成抽象语法树,所以没法使用定义在当前行以后的函数。因此,shell脚本中函数和函数之间最好没有什么关系,非要有关系,那就一定是个顺序关系。
shell的变量没有作用域
任何一个函数,只要被执行过后,函数中的变量就会成为类似全局变量的概念,在shell执行到最后一行之前,是一直驻留的,因此,不要随便定义变量,不要随意使用之前函数中出现的变量,避免将来看不懂。
shell的函数没有返回值
不同于其他编程语言,shell函数是没有返回值的,确切的说是没有返回值类型。shell只会返回数字表示成功失败。0成功1失败,通常有人习惯使用exit 指定错误数字。
shell是面向过程的
shell没有类的概念,shell也没有包的概念,通常都是一个文件搞定事情。
作文
正如上文所说,shell语言没有捷径,唯有更懂操作系统,多看别人的脚本,不断提升。这就和我们读书的时候写作文是一样的。作文是一种艺术熏陶的体现。对文学艺术体会的越深刻,手法使用的越多,就越容易让老师感到欣慰,得到高分。
多写不会手生
shell脚本一定要多写,例如if,case之流。它们是少数shell脚本的语法,但是确实交互感很差,所有的语法都有“闭标签”,新手不注意,很容易倒在脚本的错误中。熟练了,怎么写都不会出错。
多看汲取灵感
一定要多看别人的脚本,尤其是大厂的脚本,确实能够让人眼前一亮。工作中我们寻求的是把常用的命令沉淀下来形成脚本,需求是比较简单的,而且没有过多适配性。多看适配性强的代码,也很容易让自己写出高水平的脚本来。
多悟体会艺术
如果只是依葫芦画瓢,做脚本的搬运工是很难再进一步提高的。因为shell太灵活,所以并没有所谓好脚本这个标准。只有自己写出最合适的脚本才是最重要的,因此需要透过代码,看到其内部的艺术感。
shell技巧与艺术赏析
函数框架
#!/bin/bash
set -e
function a(){
}
function b(){
}
function c(){
}
a
b
c
- set -e 使得脚本报错就停止,这可以避免错误蔓延,降低反向修复的困难。
- 函数在前,尽量函数之间没有调用关系。
- 函数框架式脚本是希望将所有的函数执行一遍就可以实现功能,这里体现了代码分块的思想。
- 最后一个函数,如果有需求,也可以i变成控制器,用来决定如何调用上面的函数。(当然,这种情况,最后就只有一个函数作为入口被调用)。
连续判断取值
PYTHON_HOME="${BASE_DIR}/venv/bin"
[ ! -e "${PYTHON_HOME}/python" ] && PYTHON_HOME=/usr/local/python/bin
[ ! -e "${PYTHON_HOME}/python" ] && PYTHON_HOME=/usr/python/bin
[ ! -e "${PYTHON_HOME}/python" ] && PYTHON_HOME=/opt/python/bin
[ ! -e "${PYTHON_HOME}/python" ] && unset PYTHON_HOME
- 由于不同发行版的Linux安装软件的默认地址可能不同,配置文件路径也可能不同,因此采用试错的方法,确定配置文件的位置。
- 如果找到了配置文件,则之后几行的判断结果都为false
- 如果找不到配置文件,则走到最后,unset变量
短参数
while getopts ":h:a:" opt; do
case $opt in
h)
VAL1=$OPTARG
;;
a)
VAL2=$OPTARG
;;
?)
echo "Unknown parameter,you can use home|action parameter"
exit 1
;;
esac
done
- 支持脚本使用时传入短参数,例如 sh xxx.sh -h help -a action,则最后VAL1变量值为help,VAL2值为action
技巧
取随机数
# n为数字,数字越大,生成的随机数则越大
openssl rand -base64 n
前提是安装openssl,因为几乎都有安装,倒也不是大问题,但建议使用yum -y install openssl检查一下。
文件备份
date +%m-%d-%y_%_H:%M:%S
文件备份中使用日期命令,让最后形成的备份文件名有一定的直观含义。
写配置文件
cat << EOF > xxx.conf
...
EOF
基于配置文件内容不适合局部替换,需要大量重写或者追加(> xxx.conf 改为 >> xxx.conf),这种方法比复杂的sed效果要好
判断上一步操作是否成功
if [ $? -eq 0 ]; then
echo "succeed"
else
echo "failed"
fi
Linux没有消息就是好消息,错误消息其执行返回值不等于0.利用这个特性,我们可以大胆通过试错来得到我们想要的结果.需要注意要把错误输出写到null设备中。
id ${APP_USER} >& /dev/null
if [ $? -eq 0 ]; then
echo "系统中已经存在nginx用户"
else
useradd -M -s /sbin/nologin ${APP_USER}
fi