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 脚本可以使用任意一款纯文本编辑器进行编程开发. 常见的前端开发编 ...
随机推荐
- 【JAVA习题三】求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加
import java.util.Scanner; public class a加aa加aaa { public static void main(String[] args) { // TODO A ...
- .NET Core HttpClient源码探究
前言 在之前的文章我们介绍过HttpClient相关的服务发现,确实HttpClient是目前.NET Core进行Http网络编程的的主要手段.在之前的介绍中也看到了,我们使用了一个很重要的 ...
- Java IO(九)FilterInputStream 和 FilterOutputStream
Java IO(九)FilterInputStream 和 FilterOutputStream 一.介绍 FilterInputStream 和 FilterOutputStream 是过滤字节输入 ...
- 线程池 & 线程调度
线程池1. 第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之 一执行每个提交的任务, 通常使用 Executors 工厂方法配置. 2. 线程池可以解决两个 ...
- & 加密
接口参数中sign加密方式: 1. 签名算法使用SHA256: 2. 服务方和消费方都需要校验签名: 3. 签名生成步骤: 第一步,设所有发送或者接收到的数据为集合M1,将集合M1内非空参数值的参数按 ...
- Java-语言基础梳理
1.java命名规范 包名:全小写 类名,接口名:首字母大写 变量名,方法名:第一个单词皆字母小写,后面单词首字母大写 常量名:所有字母都大写 2.变量 2.1 注意事项 作用域:一对{}之间有用 必 ...
- 【Hadoop】hdfs,剖析文件上传
文件上传原理图 剖析文件写入 1.客户端(client)通过对DistributedFileSystem对象调用create()来新建文件: FSDataOutputStream outputStre ...
- 判断IP地址的合法性
每台计算机都有独一无二的编号,称为ip地址,每个合法的ip地址由‘.’分隔开的4个数字组成,每个数字的取值范围为0--255 输入一个字符串,判断其是否为合法的IP地址,若是输出‘YES’,否则输出‘ ...
- Java实现 LeetCode 805 数组的均值分割 (DFS+分析题)
805. 数组的均值分割 给定的整数数组 A ,我们要将 A数组 中的每个元素移动到 B数组 或者 C数组中.(B数组和C数组在开始的时候都为空) 返回true ,当且仅当在我们的完成这样的移动后,可 ...
- Java实现 蓝桥杯 传纸条
题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个mm行nn列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运 ...