Shell脚本书写规范
Shell脚本书写规范
代码风格规范
#!/usr/bin/env bash
代码注释
#!/usr/bin/env bash
#########################################################
# Function :Rotate application logs #
# Platform :All Linux Based Platform #
# Version :1.0 #
# Date :2017-07-28 #
# Author :Stephen.Shi #
# Contact :sp@meitu.com #
# Company :MeiTu #
#########################################################
参数规范
当我们的脚本需要接受参数的时候,我们一定要先判断参数是否合乎规范,并给出合适的回显,方便使用者了解参数的使用。 最少,最少,我们至少得判断下参数的个数
# Uasge
help_msg(){
echo "Usage: $0 <server_group_to_operate>"
}
# arg judge
if [[ $# -ne 1 ]]
then
help_msg
else
echo "參數不符合規範,過多"
fi
环境变量
一般情况下我们会将一些重要的环境变量定义在开头,确保这些变量的存在。
source /etc/profile
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/apps/bin/"
变量的定义和引用
1. 名称长的变量尽量使用驼峰写法或者使用小写字母+下划线的方式进行命名
2. 常量的定义推荐使用大写字母进行命名
3. 在引用变量时为了避免不必要的麻烦,尽量使用${变量名}的方式进行引用
缩进规矩
缩进同一
命名标准
文件名规范,以.sh结尾,方便识别
通过脚本名称最好能够直接读出脚本的用途,比如常用xx服务的启动脚本可命名为
start_xx.sh
,oo服务的监控脚本可命名为oo_monit.sh
,告警脚本可名称为abc_alert.sh
变量名字要有含义,不要拼错,尽量使用英文
统一命名风格,用驼峰或者下划线连接,**==推荐==**使用小写字母加下划线的方式
编码统一
在写脚本的时候尽量使用UTF-8编码,能够支持中文等一些奇奇怪怪的字符。 尽管脚本中可以写中文,但是在写注释以及打log的时候还是尽量英文,毕竟有些机器还是没有直接支持中文的,打出来可能会有乱码。
执行权限
chmod +x xxxx.sh
日志和回显
一些debug级别的信息,在脚本调试结束后需要关闭。
日志输出要带时间、要带时间、带时间,在写定时任务用的脚本时尤为重要
日志的输出推荐使用tee -a ${log_file}的方式,可以直接在main函数入口处添加日志输出。
脚本日志输出样例
logfile="/var/log/log_clean.log"
# define functions
function xx(){
echo "xx"
}
function oo(){
echo "oo"
}
# define main function
function main(){
xx
oo
}
# invoke main function
main|tee -a ${logfile}
太长要分行
# 例如
./configure \
–prefix=/usr \
–sbin-path=/usr/sbin/nginx \
–conf-path=/etc/nginx/nginx.conf \
代码有效率
sed -n '1p' file # 会读取整个文件,效率慢
sed -n '1p;1q' file # 只读取第一行,效率快
真正正确的用法应该是使用 `head -1 file`
勤用双引号
#!/bin/sh
# 已知当前文件夹有一个a.sh的文件
var="*.sh"
echo $var
echo "$var
#### 他的运行结果如下:
a.sh
*.sh
函数入口
#!/usr/bin/env bash
func1(){
#do sth
}
func2(){
#do sth
}
main(){
func1
func2
}
main "$@"
# 实现类似的main函数,使得脚本的结构化程度更好
# 另外在传递参数的时候可能会遇到参数接收位置的变化, 记得使用 `shift` 函数。
考虑作用域
#!/usr/bin/env bash
var=1
func(){
var=2
}
func
echo $var
# 输出结果: 2
因此,相比直接使用全局变量,使用 `local` `readonly` 这类的命令,
其次我们可以使用 `declare` 来声明变量。 这些方式都比使用全局方式定义要好。
巧用heredocs
heredocs
cat>>/etc/rsyncd.conf << EOF
log file = /usr/local/logs/rsyncd.log
transfer logging = yes
log format = %t %a %m %f %b
syslog facility = local3
EOF
脚本路径
script_dir=$(cd $(dirname $0) && pwd) # cd进当前脚本的目录然后再pwd
script_dir=$(dirname $(readlink -f $0 )) # 直接读取当前脚本的所在路径
代码要简短
能一条命令解决的问题绝不用两条命令解决 ----> 牵涉到代码的可读性、执行效率
其他小tip
路径尽量保持绝对路径,绝多路径不容易出错,如果非要用相对路径,最好用./修饰
优先使用bash的变量替换代替
awk sed
,这样更加简短简单的if尽量使用
&&
和||
,写成单行。比如[[ x > 2]] && echo x
当export变量时,尽量加上子脚本的
namespace
,保证变量不冲突会使用trap捕获信号,并在接受到终止信号时执行一些收尾工作
使用
mktemp
生成临时文件或文件夹利用
/dev/null
过滤不友好的输出信息会利用命令的返回值判断命令的执行情况
使用文件前要判断文件是否存在,否则做好异常处理
不要处理
ls
后的数据(比如ls -l | awk '{ print $8 }'
),ls的结果非常不确定,并且平台有关读取文件时不要使用for loop而要使用while read
小技巧
封装函数有必要 使用静态变量声明
readonly
和local
修饰变量 使用$()代替`(反单引号) 使用[[]]代替[]
bash -n test.sh # 用-n对脚本进行语法检查, echo不是唯一的调试方法
bash -v test.sh # 用-v跟踪脚本里的每个命令的执行
bash -x test.sh # 用-x跟踪脚本里的每个命令的执行,并附加扩充信息
set -o nounset # 遇到不存在的变量,终止脚本的执行
set -o errexit # 遇到执行出错,终止脚本的执行
set -o verbose # 打印读入shell的输入行
set -o xtrace # 执行命令之前打印命令
date "+%Y-%m-%d %H:%M:%S" # 输出当前时间:年月日 时分秒
read x # 和用户交互,读入输入变量
Last updated