shell脚本--代码风格规范及技巧
Posted 生信学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell脚本--代码风格规范及技巧相关的知识,希望对你有一定的参考价值。
shell脚本--代码风格规范及技巧
命名有标准
文件名规范,以.sh结尾,方便识别
编码要统一
在写脚本的时候尽量使用UTF-8编码,能够支持中文等一些奇奇怪怪的字符。不过虽然能写中文,但是在写注释以及打log的时候还是尽量英文,毕竟很多机器还是没有直接支持中文的,打出来可能会有乱码。
这里还尤其需要注意一点,就是当我们是在windows下用utf-8编码来写shell脚本的时候,一定要注意这个utf-8是否是有BOM的。默认情况下windows判断utf-8格式是通过在文件开头加上三个EF BB BF字节来判断的,但是在Linux中默认是无BOM的。因此如果我们是在windows下写脚本的时候,一定要注意将编码改成Utf-8无BOM,一般用notepad++之类的编辑器都能改。否则,在Linux下运行的时候就会识别到开头的三个字符,从而报一些无法识别命令的错。
开头有shebang
所谓shebang其实就是在很多脚本的第一行出现的以”#!”开头的注释,他指明了当我们没有指定解释器的时候默认的解释器,一般可能是下面这样:
#!/bin/bash
写出健壮Bash Shell脚本技巧
set -e
set -u
set -o pipeline
(1)在"set -e"之后出现的代码,一旦出现了返回值非零,整个脚本就会立即退出。 (2)set -u,当你使用未初始化的变量时,让bash自动退出 (3)set -o pipefail 设置了这个选项以后,包含管道命令的语句的返回值,会变成最后一个返回非零的管道命令的返回值。听起来比较绕,其实也很简单:
例如test.sh
set -o pipefail
ls ./a.txt |echo "hi" >/dev/null
echo $?
运行test.sh,因为当前目录并不存在a.txt文件,输出: ls: ./a.txt: No such file or directory 1 # 设置了set -o pipefail,返回从右往左第一个非零返回值,即ls的返回值1
注释掉set -o pipefail 这一行,再次运行,输出: ls: ./a.txt: No such file or directory 0 # 没有set -o pipefail,默认返回最后一个管道命令的返回值
工作路径
我们会先获取当前脚本的路径,然后一这个路径为基准,去找其他的路径。
work_dir=$1
reference=${work_dir}/data/reference/TAIR10_chr_all.fas
环境变量PATH
一般情况下我们会将一些重要的环境变量定义在开头,确保脚本中使用的命令能被bash搜索到。
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
运行脚本
chmod +x ./test.sh #给脚本权限
./test.sh #执行脚本
Shell中的变量
“=”前后不能有空格
定义时不用$,使用时需要$,且推荐给所有变量加上花括号{}
脚本的参数
先定义具体含义,后使用
gene_file=$1
${gene_file}
代码有注释
简述某一代码段的功能
各个函数前的说明注释
太长要分行
在调用某些程序的时候,参数可能会很长,这时候为了保证较好的阅读体验,我们可以用反斜杠来分行:
./configure \
–prefix=/usr \
–sbin-path=/usr/sbin/nginx \
–conf-path=/etc/nginx/nginx.conf \
日志
例如:
./sub_gtf.shell gene.txt Homo_sapiens.GRCh38.89.chr.gtf >logfile 2>&1
1 :表示stdout标准输出,系统默认值是1,所以">logfile"等同于"1>logfile" 2 :表示stderr标准错误 & :表示等同于的意思,2>&1,表示2的输出重定向等同于1
回显
例如
if [[ $# != 2 ]];then
echo "Parameter incorrect."
exit 1
fi
当执行:
./sub_gtf.shell gene.txt
因为参数数目不对,输出Parameter incorrect.至屏幕
当执行:
./sub_gtf.shell gene.txt >logfile 2>&1
同样参数数目不对,但输出Parameter incorrect.至日志
函数相关
巧用main函数,使得代码可读性更强
#!/bin/bash
func1(){
#do sth
}
func2(){
#do sth
}
main(){
func1
func2
}
main "$@"
考虑作用域
shell中默认的变量作用域都是全局的,比如下面的脚本:
#!/usr/bin/env bash
var=1
func(){
var=2
}
func
echo $var
他的输出结果就是2而不是1,这样显然不符合我们的编码习惯,很容易造成一些问题。
因此,相比直接使用全局变量,我们最好使用local, readonly这类的命令,其次我们可以使用declare来声明变量。这些方式都比使用全局方式定义要好。
local一般用于局部变量声明,多在在函数内部使用。
(1)shell脚本中定义的变量是global的,其作用域从被定义的地方开始,到shell结束或被显示删除的地方为止。
(2)shell函数定义的变量默认是global的,其作用域从“函数被调用时执行变量定义的地方”开始,到shell结束或被显示删除处为止。函数定义的变量可以被显示定义成local的,其作用域局限于函数内。但请注意,函数的参数是local的。
(3)如果同名,Shell函数定义的local变量会屏蔽脚本定义的global变量。
使用举例:
#!/bin/bash
function Hello()
{
local text="Hello World!!!" #局部变量
echo $text
}
Hello
只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
例如:
readonly myUrl
myUrl="http://www.runoob.com"
declare
-r 只读 (declare -r var1与readonly var1作用相同)
declare -r var1
-i 整数
declare -i number
-a 数组
declare -a indices
-f 函数
declare -f
函数返回值
在使用函数的时候一定要注意,shell中函数的返回值只能是整数,估计是因为一般情况下一个函数的返回值通常表示这个函数的运行状态,所以一般都是0或者是1就够了,因此就设计成了这样。不过,如果非得想传递字符串,也可以通过下面变通的方法:
func(){
echo "2333"
}
res=$(func)
echo "This is from $res."
这样,通过echo或者print之类的就可以做到传一些额外参数的目的。
使用新写法
这里的新写法不是指有多厉害,而是指我们可能更希望使用较新引入的一些语法,更多是偏向代码风格的,比如
尽量使用func(){}来定义函数,而不是func{}
尽量使用[[]]来代替[]
尽量使用$()将命令的结果赋给变量,而不是反引号
在复杂的场 下尽量使用printf代替echo进行回显
事实上,这些新写法很多功能都比旧的写法要强大,用的时候就知道了。
其他小tip
读取文件时不要使用for loop而要使用while read
简单的if尽量使用&& ||,写成单行。比如[[ x > 2]] && echo x
利用/dev/null过滤不友好或者无用的输出信息
例如 if grep 'pattern1' some.file > /dev/null && grep 'pattern2' some.file > dev/null then echo "found 'pattern1' and 'pattern2' in some.file" fi
/dev/null :代表空设备文件
安装shellcheck
ShellCheck, a static analysis tool for shell scripts
使用方式(1)网页版:
http://www.shellcheck.net
github仓库:
https://github.com/koalaman/shellcheck
下载安装: wget -q https://storage.googleapis.com/shellcheck/shellcheck-latest.linux.x86_64.tar.xz xz -d shellcheck-latest.linux.x86_64.tar.xz tar -xvf shellcheck-latest.linux.x86_64.tar echo 'export PATH=/home/wangdong/softwares/shellcheck:$PATH'>>~/.bashrc source ~/.bashrc
使用方式(2)终端:
shellcheck yourscipts
Shell不能做什么
需要精密的运算的时候
需要语言效率很高的时候
需要一些网络操作的时候
总之Shell就是可以快速开发一个脚本简化开发流程,并不可以用来替代高级语言
参考:
(1)https://blog.mythsman.com/2017/07/23/1/
(2)https://github.com/koalaman/shellcheck
以上是关于shell脚本--代码风格规范及技巧的主要内容,如果未能解决你的问题,请参考以下文章