shell开发规范

Posted vnx

tags:

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

版本1.0版,参考网上的一些文章规整而来。后期打算继续修改。完成一篇适合自己的shell开发规范。

最新编辑时间:2017.6.25

一、 命名规范

1、 版本和运行参数

1) 脚本开始之前以注释形式说明版本号;(推荐)
2) 如果调用其他工具,还需说明工具的版本号;(推荐)
3) 为脚本添加必须的运行参数,类似于C程序的运行参数,可使用getopt的方式取得运行参数值,如基本的参数有:-v - 版本号;-h – 帮助信息;(推荐)

2、 变量命名

1) 变量命名要前后统一,建议使用全部大写字母,如APACHE_ERR_NUM;语义要清晰,能够正确表达变量的含义,过长的英文单词可采用前几个字符代替。多个单词连接使用“_”号连接, 引用时,必须以${APACHE_ERR_NUM}方式引用;
2) 避免无含义字符或数字: 例如下面的数字22, 并不知道其确切含义.

COUNT=`grep keywords file`
if [ ${COUNT} –ne 22 ]
then
    Do Something
fi

3) 全局变量和局部变量:
i. 如需要使用全局变量,则在变量加前缀g,如g_WORK_DIR,在变量使用时,使用{}将其括起,即${VARIABLE}
ii. 局部变量的使用:脚本在定义在一个函数(function)中的变量我们称之为局部变量,必须要以local方式进行声明,使之只在本函数中作用有效,以免造成在函数中的命名与程序中变量重名,造成对程序中正常变量的不正常操作。如

function TestFunc()
{
    local i
    for((i=0;i<n;i++))
    do
        do something
    done
}    

4) 变量合并:当某些变量或配置项要组合起来才有意义时,如文件的路径和文件名称,建议将要组合的变量合并到一起赋值给一个新的变量,这样既方便之后的调用,也为以后进行修改提供了方便。

3、 函数命名

1) 函数命名使用单词首字母大写,如CreateFile,并且要语义清晰,如使DumpDataToFile()代替DataFile()。同时要注意前后缀,如后缀为Max则为最大值,Min为最小值,前缀Is为判断型函数,Get则为取值函数,这样有益于对函数的返回理解更清晰。
2) 注意在使用单词缩写时,也使用首字母大写,如GethtmlContent而并非GetHTMLContent,这样有利于命名规则的统一。

4、 脚本(模块)命名

1) Shell脚本使用统一后缀:.sh
2) 所有模块的启动和停止脚本必须统一命名为start_{模块名}.sh和stop_{模块名}.sh
3) 模块(及其脚本和二进制程序) 命名应该代表其特性和功能,禁止使用个人名字缩写等形式命名。
4) 模块中的脚本和二进制程序命名禁止和其他脚本和二进制程序重名。
5、 临时文件的命名
尽量避免临时文件,如果一定要使用临时文件,请使用脚本的pid作为后缀,并在脚本结束时清除临时文件。例如:

PID=$$
TMP_FILE=”tmp_keywords.”${PID}
grep keywords files > ${TMP_FILE}
.....
#end
rm ${TMP_FILE}

二、 代码风格

1、 代码框架

1) 解释器声明最好为 #!/bin/sh(推荐),如果不是再加入crontab计划任务时要格外注意执行环境。
配置文件/库函数的引用 source foo_conf.sh
2) 主过程只实现程序主干,功能实现应该封装在子函数中;
3) 对于能独立执行的脚本需要有usage和version函数,可以输出脚本用法和版本信息(推荐)

2、 函数

1) 函数定义时在函数名前加上function保留字
2) 显示函数返回:在函数的每个分支在函数的每个分支都包含显示的return语句,并跟上返回值。即使是不关心返回值的函数,也可能在后续调用时无意的去判断它的返回值并进行一系列动作,这种不必要的麻烦我们在一开始就应该注意,显示的写return语句并不会带来多少负担,相反的,它能让函数逻辑更加清晰和严谨。

3、 条件语句与循环

1) 在使用条件语句及循环时,尽量使用统一格式,而不是使用“;”分隔;

if [ -d abc ]
then
    Do something
fi

while [ 1 ]
do
    Do something
done

for i in *
do
    Do something
Done

2) 尽量使用每一行一条语句,而不是使用”;”将多个语句隔开。尽可能多的判断操作是否成功,并对其进行相应处理,如DoSth && DoRight || DoWrong 。这样使用简化了语句,也使语义变得更加清晰明了;
3) 要使用简单的语句。特别是应避免多重管道的命令,多重管道的命令在每次阅读时都需要关注每条管理的信息,直到最后一个命令,如果将其拆分成多个语句,可读性强;
4) 承接上面第3)点,脚本中经常有grep cut awk sed等命令一起配合的复杂语句。这时应尽量考虑简化语句。例如多个grep awk操作经常可以由1个sed替代。

4、 缩进

在使用条件语句时,每进行一层循环或是循环内部的操作,就使用一个缩进,缩进一般用TAB键。

if [ -d abc ]
then
cd abc
    if [ -d bcd ]
    then
        DoSth
    fi
cd ..
fi

三、 注释规范

1、 文件/模块说明

说明模块主要用途,版本信息,输入输出文件,依赖工具及其版本信息, 前后流程脚本(可选),格式统一即可,不做强制要求,可如下:

##! @TODO: url analyse 
##! @VERSION: 1.0 
##! @AUTHOR: MM;BB 
##! @FILEIN: data/url.crawl
##! 由dedup_crawl.sh生成, 格式为... 
##! @FILEOUT: result/GOOD_GRP
##! 通过检测的无问题alias组 ##! @FILEOUT: result/WRONG_GRP
##! 未通过检测的有问题alias组 
##! @DEP: wget 1.10.2 
##! @DEP: lftp 3.0.6 
##! @PREV: dedup_crawl.sh 
##! @NEXT: dedup_update.sh

注释中的路径给出的相对路径都必须是相对于该模块安装时生成的根目录。
example:
模块seek_url安装在PATH目录下,安装后生成了新的目录seek_url,那么所有的相对路径都是相对于PATH/seek_url/的。

推荐为脚本的配置和在处理流程中位于本脚本之前和之后的脚本写注释。以便于测试和新人熟悉模块流程。 (推荐)

example:
##! @conf conf/config_dedup_check.sh
##! dedup-check的模块范围的配置文件
##! 这里主要读取BINPATH 和 ALIAS_FILE设置
处理流程中在本脚本之前的脚本名
##! @prev dedup_crawl.sh
处理流程中在本脚本之后的脚本名
##! @next dedup_update.sh

所有脚本都要在脚本或配置文件中写明:依赖的模块、工具、脚本 (必须)

example:
##! @dep spuser-ftool
##! 简要说明
##! @dep read_di
##! 简要说明

2、 重要函数说明

对于重要函数,需说明函数用途,参数,返回值,作者,版本,格式也不做强制要求,可如下:

##! @TODO: get hostname 
##! @AUTHOR: somebody 
##! @VERSION: 1.0 
##! @IN: $1 => ip 
##! @IN: $2 => port 
##! @OUT: 0 => success; 1 => failure


3、 其它

脚本中一定要有十分详细的注释,包括变量的定义,函数的定义,返回值的函义,每步操作的目的等,这方面尤其要加强。注释尽量使用标准的英文或是详细的中文说明。

四、 日志规范

1. 脚本和C程序一样,需要有日志记录脚本的运行状态,操作过程等。推荐:每条日志要以记录的当前时间为开头,然后是记录的日志描述信息,描述信息要包括日志的级别,如FATAL,WARNING,NOTICE等,还要保留充分完整,无歧义的上下文信息,尤其是FATAL和WARNING日志更是如此。
2. 每个脚本程序要有自己的日志文件,不要将多个脚本的日志记录到同一个日志文件中,即使这些脚本之间有依赖关系也不可以。

五、 接口文件规范

1. 所有模块之间的接口文件必须在生成的同时生成md5校验码,并且一个文件对应一个md5文件,md5文件的命名为接口文件名后加上“.md5”。
2. 机器之间的数据拷贝推荐使用wget,尽量减少scp和ssh的使用。但是只要是wget获取远程数据,必须使用md5校验数据的完整性。
3. 推荐所有接口文件都进行格式检查,对空文件和不存在的文件判断方式应该一致。
4. 推荐接口文件最好有相应的flag文件指明生成时间,以便检查和容错。

六、 配置规范

在多个脚本程序协同工作时,应该尽量使用公共函数库来完成近似的功能,使用配置文件统一变量及外部数据的引用,这样便于日后的功能升级及环境的变更操作。如果使用了公共函数库,建议在配置文件中添加公共函数库脚本的路径,即使是在当前目录下也要进行配置,如FILENAME_PATH=”./”,脚本中的公共函数库及配置文件使用source ${FILENAME_PATH}/filename.sh方式加载。

1、 配置文件

1) 所有的脚本必须把功能和配置明确分开;
2) 配置文件,以conf_脚本名.sh 来命名(推荐);
3) 原则上,配置项放在目录conf/底下,引用时通过source conf/conf_spdata.sh的形式加载;
4) 配置项的命名规范与变量命名规范相同,要求所有的配置项应都有值,避免内容为空的配置项出现,配置项的内容由双引号括起,如:
OUTPUT_FILE_PATH=”./output”
5) 路径参数的配置:脚本中经常需要配置路径,本地路径或者远程机器的路径。路径的配置强烈推荐使用全局路径,全局路径的配置建议以“/”开头,末尾没有“/”,脚本中使用路径的配置项时,必须以“/”分隔不同的配置项,变量或通配符等,不可以省略“/”。
6) 易变参数的配置:易变参数中主要是指状态报告和报警的收件人,都必须写成配置。
7) 机器名,端口和用户名引用:所有线上脚本都必须通过配置引用机器名、端口和用户名等信息。
8) 二进制程序引用:当模块中需要调用非本模块维护的二进制程序/脚本时,都必须通过一个可配置的路径来间接引用,禁止直接复制其它模块的二进制程序到自身目录使用。

2、 配置项的检查

1) 脚本中要检查配置项是否为空,尤其是一些重要的,影响下面脚本正常运行的配置项,必须要进行是否为空的检查,避免配置文件中有遗漏等错误,检查格式可如下:

if [ -z ${OUTPUT_FILE_PATH} ]
then
    Do something, such as write log
fi

七、 报警规范

1) 报警邮件的标题(和短信报警)必须为如下格式:
[报警级别] [服务名] [机器名] [模块名] 报警信息 [时间]
示例:

echo "[error][image][$(hostname -s)][${0}][ urlmerge for group ${group_no} failed ][$(date +\'%Y-%m-%d %H:%M:%S\')]"


2) 报警邮件的发件人必须真实,报警短信必须包括发件机器名。不容许在A机器上发出以B机器名义的报警,推荐使用 `hostname` 命令

3) 线上模块报警邮件(或短信)的收件人必须包括模块负责人,指导人,op,禁止将模块报警(或短信)发给全组。 每个包含信息相同的报警原则上不得多于3条。(必须)

八、 其它细节

1) 涉及依赖的机器,需以配置的形式出现在脚本的配置文件;禁止在脚本中随处定义机器,这样不易于维护,也容易出错; (必须)
2) 必须要有基本的日志输出;(必须)
3) 要关注脚本的效率和系统消耗,综合、平衡的考虑;(必须)
4) 函数参数传递:在调用函数时,向函数传递的参数如果是以变量的方式进行传递,必须使用双引号将变量括起,这是为了防止某个变量中含有多个以空格分隔的字段,导致函数误认为是多个参数,如报警邮件和报警短信的配置项,使用方式如下:

Alert “some thing” “${LOG}” “${ALERT_MAIL}” “${ALERT_MOBILE}”

5) 避免cat 大文件。比如 for id in `cat id_file`;do … ;done。而是采用readline形式读入文件。(必须)
6) 所有上一级命令需用 if [ $? ] 来判断返回值,对于异常分支,需有相应的处理策略:或是打印warning日志,或是报警退出/重试。(必须)
7) Sort超过1G的数据文件时,必须用-T指定其临时文件夹,推荐在使用到sort的时候就使用-T指定到自身的TMP目录。(必须)
8) 避免使用大的while/for循环,如果实在需要,请考虑用awk命令替代。(推荐)
9) 需要配置crontab的脚本,要注意crontab的时间可能会有前后1分钟之内的波动,对于时间精度要求较严格的脚本,不建议配置在比如00分钟这样的时刻。
10) 对于一系列有严格依赖关系的命令,建议使用 && 来处理,比如:make mydir && mv myfile mydir;并且对于有前后次序的脚本,禁止采用后台运行 & 命令。(推荐)
11) 脚本运行前后,注意清除过期数据(上次运行生成的数据)(推荐)
12) 脚本开始运行之前需明确当前运行路径,例如cd /home/img/img;同时必须明确数据生成路径,例如 data/spdata_stat/splendid.list;(必须)
13) 对于功能较为复杂的脚本,考虑使用一些函数对功能点进行封装。这样可以使脚本清晰易读;(推荐)
14) 任何出错情况必须将出错信息打印到日志中;严重的错误必须以邮件或短信报警的形式发出。(必须)
15) 对于逻辑比较复杂的脚本,可以使用 set -x 来打印命令执行情况,便于调试和排错 (推荐)
16) wget之前,注意先删除本地文件;(推荐)
17) 拷贝比较大的文件时,最好先将文件cp到一个临时文件夹,然后mv到目标文件夹。这样可以避免下游模块读取到了不完整的文件。(推荐)
18) mv,cp等命令,注意要使用 –r –f等递归和强制命令(必须)
19) 脚本中,要注意对单引号‘’双引号“”的转义。不明确转义含义的,最好在单元自测的时候多加小心。(推荐)
20) 使用paste命令的时候,注意分清楚,文件是使用tab还是空格作分隔符。
21) 使用ps axuw|grep来获取信息时,注意ps打印的最大宽度。建议多打几个w,例如ps axuwwww|grep … ;(推荐)
22) 使用sort,uniq,join,comm等命令时,要注意两点:1,是否要求排序(例如uniq,join,comm要求输入文件是排好序的);2,如果排序,要求以什么方式排序(例如,comm要求输入文件是字典序的,而不是数值序);(推荐)
23) 字段匹配日志的时候,要注意特殊字符,例如

spurl[stargirls]spid[2534969]opusr [stargirls]opuid[6545440]

  这样的日志,如果只使用[ 和]作分隔符,就会有问题,因为spurl中就可能含有[或]等特殊字符,从而导致后面的匹配全部出错。
24) 对特殊信号的处理:在脚本运行时,我们有时会临时中断此脚本(ctrl+c),如果脚本在做多个文件操作,并生成一些临时文件,那么我们在中断脚本时,就需要恢复或清除一些过程文件。在复杂脚本中,要对一些中止的信号进行特殊的处理。

参考:

 本文地址:http://www.cnblogs.com/chinas/p/7073061.html,转载请注明出处,谢谢!!!

以上是关于shell开发规范的主要内容,如果未能解决你的问题,请参考以下文章

Shell脚本的书写规范与优秀的开发习惯

Shell编程之代码规范

Shell编程之代码规范

shell脚本开发基本规范及习惯

编写Shell脚本的最佳实践,规范一

php Yoast SEO规范输出的代码片段