Shell语法规范
- ver:1.0
- 博客:https://www.cnblogs.com/Rohn
- 本文介绍了Shell编程的一些语法规范,主要参考依据为谷歌的Shell语法风格。
背景
博客:https://www.cnblogs.com/Rohn
使用哪一种Shell
可执行文件必须以 #!/bin/bash 和最小数量的标志开始。请使用 set 来设置shell的选项,使得用 <script_name>调用你的脚本时不会破坏其功能。
推荐使用:
#!/usr/bin/env bash
env一般固定在/usr/bin目录下,而其余解释器的安装位置就相对不那么固定。
限制所有的可执行Shell脚本为bash使得我们安装在所有计算机中的shell语言保持一致性。
无论你是为什么而编码,对此唯一例外的是当你被迫时可以不这么做的。其中一个例子是Solaris SVR4包,编写任何脚本都需要用纯Bourne shell。
[root@test ~]# echo $SHELL
/bin/bash
什么时候使用Shell
使用Shell需要遵守的一些准则:
- 如果你主要是在调用其他的工具并且做一些相对很小数据量的操作,那么使用Shell来完成任务是一种可接受的选择。
- 如果你在乎性能,那么请选择其他工具,而不是使用Shell。
- 如果你发现你需要使用数据而不是变量赋值(如 ${PHPESTATUS} ),那么你应该使用Python脚本。
- 如果你将要编写的脚本会超过100行,那么你可能应该使用Python来编写,而不是Shell。
请记住,当脚本行数增加,尽早使用另外一种语言重写你的脚本,以避免之后花更多的时间来重写。
注释
博客:https://www.cnblogs.com/Rohn
Bash只支持单行注释,使用#开头的都被当作注释语句。
顶层注释
每个文件必须包含一个顶层注释,对其内容进行简要概述。版权声明和作者信息是可选的。
例如:
#!/usr/bin/env bash
# Author: Rohn
# Version: 1.0
# Created Time: 2020/06/06
# Perform hot backups of MySQL databases.
- 第1行,指明解释器,使用
bash
#!叫做"Shebang"或者"Sha-bang"(Unix术语中,#号通常称为sharp,hash或mesh;而!则常常称为bang),指明了执行这个脚本文件的解释程序。当然,如果使用bash test.sh这样的命令来执行脚本,那么#!这一行将会被忽略掉。
- 第2-5行,分别为作者、版本号、创建时间、功能说明。
功能注释
任何不是既明显又短的函数都必须被注释。任何库函数无论其长短和复杂性都必须被注释。
其他人通过阅读注释(和帮助信息,如果有的话)就能够学会如何使用你的程序或库函数,而不需要阅读代码。
所有的函数注释应该包含:
- 函数的描述
- 全局变量的使用和修改
- 使用的参数说明
- 返回值,而不是上一条命令运行后默认的退出状态
例如:
#!/usr/bin/env bash
# Author: Rohn
# Version: 1.0
# Created Time: 2020/06/06
# Perform hot backups of Oracle databases.
export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin'
#######################################
# Cleanup files from the backup dir
# Globals:
# BACKUP_DIR
# ORACLE_SID
# Arguments:
# None
# Returns:
# None
#######################################
cleanup() {
...
}
TODO注释
TODOs应该包含全部大写的字符串TODO,接着是括号中你的用户名。冒号是可选的。最好在TODO条目之后加上bug或者ticket的序号。
例如:
# TODO(mrmonkey): Handle the unlikely edge cases (bug ####)
格式
博客:https://www.cnblogs.com/Rohn
缩进
缩进两个空格,没有制表符。例如:
if [ a > 1 ];then
echo '${a} > 1'
fi
行的长度和长字符串
行的最大长度为80个字符。例如:
# DO use 'here document's
cat <<END;
I am an exceptionally long
string.
END
# Embedded newlines are ok too
long_string="I am an exceptionally
long string."
管道
如果一行容不下整个管道操作,那么请将整个管道操作分割成每行一个管段。
应该将整个管道操作分割成每行一个管段,管道操作的下一部分应该将管道符放在新行并且缩进2个空格。这适用于使用管道符|的合并命令链以及使用||和&&的逻辑运算链。
例如:
# All fits on one line
command1 | command2
# Long commands
command1 \
| command2 \
| command3 \
| command4
循环
if-else语句
if和; then放在同一行,;后空一格,else单独一行,fi单独一行,并与if垂直对齐。即:
if condition; then
statement(s)
else
statement(s)
fi
for-do和while-do语句
while/for和; do放在同一行,done与while/for垂直对齐,即:
# while structure
while condition; do
statement(s)
done
# for structure
for condition; do
statement(s)
done
例如:
for dir in ${dirs_to_cleanup}; do
if [[ -d "${dir}/${ORACLE_SID}" ]]; then
log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
rm "${dir}/${ORACLE_SID}/"*
if [[ "$?" -ne 0 ]]; then
error_message
fi
else
mkdir -p "${dir}/${ORACLE_SID}"
if [[ "$?" -ne 0 ]]; then
error_message
fi
fi
done
case语句
- 通过2个空格缩进可选项。
- 在同一行可选项的模式右圆括号之后和结束符
;;之前各需要一个空格。 - 长可选项或者多命令可选项应该被拆分成多行,模式、操作和结束符
;;在不同的行。
匹配表达式比case和esac 缩进一级。多行操作要再缩进一级。一般情况下,不需要引用匹配表达式。模式表达式前面不应该出现左括号。避免使用;&和;;&符号。即:
# case structure
case in expression in
pattern1)
statement1
;;
pattern2)
statement2
;;
...
*)
statementn
;;
esac
例如:
case "${expression}" in
a)
variable="..."
some_command "${variable}" "${other_expr}" ...
;;
absolute)
actions="relative"
another_command "${actions}" "${other_expr}" ...
;;
*)
error "Unexpected expression '${expression}'"
;;
esac
只要整个表达式可读,简单的命令可以跟模式和;; 写在同一行。这通常适用于单字母选项的处理。当单行容不下操作时,请将模式单独放一行,然后是操作,最后结束符;; 也单独一行。当操作在同一行时,模式的右括号之后和结束符;;之前请使用一个空格分隔。
verbose='false'
aflag=''
bflag=''
files=''
while getopts 'abf:v' flag; do
case "${flag}" in
a) aflag='true' ;;
b) bflag='true' ;;
f) files="${OPTARG}" ;;
v) verbose='true' ;;
*) error "Unexpected option ${flag}" ;;
esac
done
变量扩展
按优先级顺序:保持跟你所发现的一致;引用你的变量;推荐用
${var}而不是$var。
例如
# Section of recommended cases.
# Preferred style for 'special' variables:
echo "Positional: $1" "$5" "$3"
echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ ..."
# Braces necessary:
echo "many parameters: ${10}"
# Braces avoiding confusion:
# Output is "a0b0c0"
set -- a b c
echo "${1}0${2}0${3}0"
# Preferred style for other variables:
echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
while read f; do
echo "file=${f}"
done < <(ls -l /tmp)
# Section of discouraged cases
# Unquoted vars, unbraced vars, brace-quoted single letter
# shell specials.
echo a=$avar "b=$bvar" "PID=${$}" "${1}"
# Confusing use: this is expanded as "${1}0${2}0${3}0",
# not "${10}${20}${30}
set -- a b c
echo "$10$20$30"
特性
博客:https://www.cnblogs.com/Rohn
命令替换
使用
$(command)而不是反引号。
嵌套的反引号要求用反斜杠转义内部的反引号。而$(command) 形式嵌套时不需要改变,而且更易于阅读。
例如:
# This is preferred:
var="$(command "$(command1)")"
# This is not:
var="`command \`command1\``"
文件名的通配符扩展
当进行文件名的通配符扩展时,请使用明确的路径。
因为文件名可能以-开头,所以使用扩展通配符./*比*来得安全得多。
# Here's the contents of the directory:
# -f -r somedir somefile
# This deletes almost everything in the directory by force
psa@bilby$ rm -v *
removed directory: `somedir'
removed `somefile'
# As opposed to:
psa@bilby$ rm -v ./*
removed `./-f'
removed `./-r'
rm: cannot remove `./somedir': Is a directory
removed `./somefile'
命名约定
博客:https://www.cnblogs.com/Rohn
函数名
使用小写字母,并用下划线分隔单词。使用双冒号
::分隔库。函数名之后必须有圆括号。关键词function是可选的,但必须在一个项目中保持一致。
如果你正在写单个函数,请用小写字母来命名,并用下划线分隔单词。如果你正在写一个包,使用双冒号 :: 来分隔包名。大括号必须和函数名位于同一行(就像在Google的其他语言一样),并且函数名和圆括号之间没有空格。
# Single function
my_func() {
...
}
# Part of a package
mypackage::my_func() {
...
}
当函数名后存在 () 时,关键词 function 是多余的。但是其促进了函数的快速辨识。
变量名
使用小写字母,循环的变量名应该和循环的任何变量同样命名。例如:
for zone in ${zones}; do
something_with "${zone}"
done
常量和环境变量名
全部使用大写字母,用下划线分隔,声明在文件的顶部。例如:
# Constant
readonly PATH_TO_FILES='/some/path'
# Both constant and environment
declare -xr ORACLE_SID='PROD'
源文件名
使用小写字母,如果需要的话使用下划线分隔单词。例如: maketemplate 或者 make_template ,而不是 make-template 。
只读变量
使用小写字母,使用 readonly 或者 declare -r 来确保变量只读。
因为全局变量在Shell中广泛使用,所以在使用它们的过程中捕获错误是很重要的。当你声明了一个变量,希望其只读,那么请明确指出。
zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
if [[ -z "${zip_version}" ]]; then
error_message
else
readonly zip_version
fi
使用本地变量
使用小写字母,使用 local 声明特定功能的变量。声明和赋值应该在不同行。
使用 local 来声明局部变量以确保其只在函数内部和子函数中可见。这避免了污染全局命名空间和不经意间设置可能具有函数之外重要性的变量。
当赋值的值由命令替换提供时,声明和赋值必须分开。因为内建的 local 不会从命令替换中传递退出码。
my_func2() {
local name="$1"
# Separate lines for declaration and assignment:
local my_var
my_var="$(my_func)" || return
# DO NOT do this: $? contains the exit code of 'local', not my_func
local my_var="$(my_func)"
[[ $? -eq 0 ]] || return
...
}
调用命令
博客:https://www.cnblogs.com/Rohn
检查返回值
对于非管道命令,使用$?或直接通过一个if语句来检查以保持其简洁。例如:
if ! mv "${file_list}" "${dest_dir}/" ; then
echo "Unable to move ${file_list} to ${dest_dir}" >&2
exit "${E_BAD_MOVE}"
fi
# Or
mv "${file_list}" "${dest_dir}/"
if [[ "$?" -ne 0 ]]; then
echo "Unable to move ${file_list} to ${dest_dir}" >&2
exit "${E_BAD_MOVE}"
fi
Bash也有 PIPESTATUS 变量,允许检查从管道所有部分返回的代码。如果仅仅需要检查整个管道是成功还是失败,以下的方法是可以接受的:
tar -cf - ./* | ( cd "${dir}" && tar -xf - )
if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then
echo "Unable to tar files to ${dir}" >&2
fi
可是,只要你运行任何其他命令, PIPESTATUS 将会被覆盖。如果你需要基于管道中发生的错误执行不同的操作,那么你需要在运行命令后立即将 PIPESTATUS 赋值给另一个变量(别忘了 [ 是一个会将 PIPESTATUS 擦除的命令)。
tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
return_codes=(${PIPESTATUS[*]})
if [[ "${return_codes[0]}" -ne 0 ]]; then
do_something
fi
if [[ "${return_codes[1]}" -ne 0 ]]; then
do_something_else
fi
Shell语法规范的更多相关文章
- shell编程规范:引用
Shell代码规范 作 者: 毕小朋 用 途: 规范Shell代码书写,方便查看与修改 博 客: http://blog.csdn.net/wirelessqa 参 考: http://www.ohl ...
- JSLint检测Javascript语法规范
前端javascript代码编写中,有一个不错的工具叫JSLint,可以检查代码规范化,压缩JS,CSS等,但是他的语法规范检查个人觉得太“苛刻”了,会提示各种各样的问题修改建议,有时候提示的信息我们 ...
- css 之 1.基本语法规范
文章转自:http://www.10wy.net/Article/CSS/CSS_list_8.html查看更多更专业性的文章请到:网页设计网 第一篇 CSS 1.基本语法规范 分析一个典型CSS的语 ...
- makefile中的shell语法
在Makefile中写shell代码有点诡异,和不同的shell语法不太一样,如果不了解,看Makefile会莫名其妙.下面总结了一些. 1:尽在Makefile文件的目标项冒号后的另起一行的代码才是 ...
- 【转】Application.mk 文件语法规范
原文网址:http://blog.sina.com.cn/s/blog_4c451e0e0100s6q4.html Application.mk file syntax specification A ...
- MySQL数据库基础(一)(启动/停止、登录/退出、语法规范及最基础操作)
1.启动/停止MySQL服务 启动:net start mysql 停止:net stop mysql 2.MySQL登录/退出 登录:mysql 参数:如果连接的是本地服务器,一般用命令:my ...
- web前端(14)—— JavaScript的数据类型,语法规范1
编辑器选择 对js的编辑器选用,有很多,能对html编辑的,也能对js编辑,比如notepad++,visual studio code,webstom,atom,pycharm,sublime te ...
- RAP Mock.js语法规范
Mock.js 的语法规范包括两部分: 数据模板定义规范(Data Template Definition,DTD) 数据占位符定义规范(Data Placeholder Definition,DPD ...
- JavaScript 中语法规范及调试
JavaScript 中语法规范及调试 版权声明:未经博主授权,内容严禁分享转载 JavaScript 开发环境 JavaScript 脚本可以使用任意一款纯文本编辑器进行编程开发. 常见的前端开发编 ...
随机推荐
- 苏浪浪 201771010120 第四周 Java基本程序设计总结
第四章 对象与类 学习目标 掌握类与对象的基础概念,理解类与对象的关系: 掌握对象与对象变量的关系: 掌握预定义类的基本使用方法,熟悉Math类.String类.math类.Scanner类.Lo ...
- Fabric进阶(一)—— 修改组织和通道的名称
组织(Org)和通道(Channel)的名称是fabric网络比较重要的两个配置参数,在fabric提供的示例中都已经设置好了这两个参数,一般组织名为"Org1"和"Or ...
- Docker入门 安装Tomcat以及报404解决方案
时间:2020/1/18 17:34:09 浏览:24 来源:互联网 记录简单的在Docker 上安装Tomcat 首先我是在云服务器上(Centos系统)安装的Docker,我们需要在https:/ ...
- php实用正则
1 Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 2 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z ...
- ES[7.6.x]学习笔记(十一)与SpringBoot结合
在前面的章节中,我们把ES的基本功能都给大家介绍完了,从ES的搭建.创建索引.分词器.到数据的查询,大家发现,我们都是通过ES的API去进行调用,那么,我们在项目当中怎么去使用ES呢?这一节,我们就看 ...
- 需求:一个页面中需要用到多个字典数据。用于下拉选项,同时,需要将其保存为json格式。以便于key,value的相互转换。记录在实现过程中踩的坑
本文涉及到的知识: Promise,all()的使用 js处理机制 reduce的用法 map的用法 同步异步 需求: 一个页面中需要用到多个字典数据.用于下拉选项,同时,需要将其保存为json格式. ...
- [wordpress使用]002_主题
使用WordPress作为博客内容管理系统有一个很大的好处是,WordPress拥有大量的优秀的免费模板.你所需要的是下载安装,和稍作修改.下面接着开始WordPress教程:WordPress主题 ...
- [优文翻译]001.真正程序员该是什么样的(How To Be A Real Programmer)
01.Real Programmers don't write specs -- users should consider themselves lucky to get any programs ...
- 百万年薪架构师一文整理RabbitMQ、ActiveMQ、RocketMQ、Kafka
一般来说,大型应用通常会被拆分成多个子系统,这些子系统可能会部署在多台机器上,也可能只是一台机器的多个进程中,这样的应用就是分布式应用.在讨论分布式应用时,很多初学者会把它和集群这个概念搞混,因为从部 ...
- 小谢第7问:js前端如何实现大文件分片上传、上传进度、终止上传以及删除服务器文件?
文件上传一般有两种方式:文件流上传和base64方式上传,毫无疑问,当进行大文件上传时候,转为base64是不现实的,因此用formData方式结合文件流,直接上传到服务器 本文主要结合vue的来讲解 ...