原文链接
英文原文:https://google.github.io/styleguide/shellguide.html
中文原文:https://zh-google-styleguide.readthedocs.io/en/latest/google-shell-styleguide/

手抄笔记用于后续查阅。


文件拓展名

❕Tip
可执行文件应该没有扩展名(强烈建议)或者使用.sh扩展名。库文件必须使用.sh作为扩展名,而且应该是不可执行的

平时脚本建议 chmod u+x script_name 给脚本添加执行权限,不用添加扩展名(Linux外编辑器可添加,如notepad++编辑保存为.sh扩展名文件可以帮助检查语法错误)


注释

文件头

❕Tip
每个文件的开头是其文件内容的描述。

每个文件必须包含一个顶层注释,对其内容进行简要概述。版权声明和作者信息是可选的

例如

#!/bin/bash
#
# 程序启动脚本

功能注释

❕Tip
任何不是既明显又短的函数都必须被注释。任何函数无论其长短和复杂性都必须被注释。

所有的函数注释应该包含:

  • 函数的描述
  • 全局变量的使用和修改
  • 使用的参数说明
  • 返回值,而不是上一条命令运行后默认的推出状态

例如

#!/bin/bash
#
# 启动程序脚本

APP_NAME="Cluster_1"

###########################
# 程序启动函数
# 全局变量:
#   APP_NAME:应用名称
# 参数:
#   无
# 返回值:
#   无
###########################
start(){
  # TODO
}

实现部分的注释

❕Tip
注释你代码中含有技巧、不明显、有趣的或者重要的部分。

针对某行复杂命令平时可以进行单行注释。

TODO注释

❕Tip
使用TODO注释临时的、短期解决方案的、或者足够好但不够完美的代码。

如果在实现时候觉得代码有缺陷用TODO注释起来。

TODO注释应该包含全部大写的字符串TODO,接着是括号中你的用户名。冒号是可选的。最好在TODO条目之后加上bug或者ticket的序号。

例如

# TODO(曹曹曹老板来了):此处可能存在不匹配的未知场景错误(bug ###)

格式

缩进

❕Tip
缩进两个空格,没有制表符。

这一点很重要,平时代码过程忌讳制表符缩进,统一使用两个空格。

行的长度和长字符串

❕Tip
行的最大长度是80个字符。

循环

❕Tip
请将; do; t henwhileforif放在同一行。

循环结构尽量一次写完整再写循环体语句。

例如

# for循环语句
for fir in ${dirs_to_cleanup}; do
  if [[ -d "${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

这里有几个细节:

  • 缩进采用两个空格
  • if [[ "$?" -ne 0 ]] 需要和[ ] 留有一个空格(必须)
  • if 语句使用的 [[ ]] 而不是 [ ]

case语句

❕Tip
- 通过两个空格缩进可选项。
- 在同一行可选项的模式右圆括号之后和结束符;;之前各需要一个空格。
- 长可选项或者多命令可选项应该被拆分成多行,模式、操作、和结束符;;在不同的行。

匹配表达式比caseesac缩进一级。多行操作要再缩进一级。一般情况下,不需要引用匹配表达式。模式表达式前面不应该出现左括号。避免使用;&;;&符号。

例如

# 匹配不同命令执行不同的函数
case "${input}" in
  start) 
    echo "开始启动程序..."
    start ${APP_NAME}
    ;;
  stop)
    echo "开始停止程序..."
    stop ${APP_NAME}
    ;;
  *)
    error "未知表达式。"
    ;;
esac

变量扩展

❕Tip
按优先级顺序:保持跟你所发现的一致;引用你的变量;推荐用${var}而不是$var

引用

❕Tip
除非需要小心不带引号的扩展,否则总是引用包含变量、命令替换符、空格或shell元字符的字符串。

使用变量时尽量带上引号,即 "${val}"

  • 推荐引用是单词的字符串(而不是命令选项或者路径名)。
  • 千万不要引用整数。
  • 注意[[中模式匹配的引用规则。
  • 请使用$@ 除非你有特殊的原因需要使用$*

特性及错误

命令替换

❕Tip
使用$(command)而不是反引号`command`

嵌套的反引号要求用反斜杠转义内部的反引号。而$(command)形式嵌套时不需要改变,而且更易于阅读。

例如

# 倾向于这种
var="$(command "$(command)")"

# 而不是这种
var="`command \`command\``"

test,[ 和 [[

❕Tip
推荐使用[[ ... ]]而不是[test,和/usr/bin/[

因为在[[]]之间不会有路径名称扩展或单词分割发生,所以使用[[ … ]]能够减少错误。而且[[ … ]]允许正则表达式匹配,而[ … ]不允许。

测试字符串

❕Tip
尽可能使用引用,而不是过滤字符串。

Bash足以在测试中处理空字符串。所以,请使用空(非空)字符串测试,而不是填充字符,使得代码更易于阅读。

例如

# 推荐 -- 匹配字符串
if [[ "${my_var}" = "some_string"]]; then
  # TODO
fi

# 推荐 -- 判断字符串是否为空
if [[ -z "${my_var}" ]]; then
  # TODO
fi

# 推荐 -- 判断字符串不为空
if [[ -n "${my_var}" ]]; then
  # TODO
fi

# 不推荐 -- 匹配字符串
if [[ "${my_var}X" = "some_stringX"]]; then
  # TODO
fi

# 不推荐 -- 判断字符串是否为空
if [[ "${my_var}" = "" ]]; then
  # TODO
fi

文件名的通配符扩展

❕Tip
当进行文件名的通配符扩展时,请使用明确的路径。

因为文件名可能义-开头,所以使用扩展通配符./* * 来得安全的多。

Eval

❕Tip
应该避免使用eval。

当用于给变量赋值时,Eval解析输入,并且能够设置变量,但无法检查这些变量时什么。

管道导向while循环

❕Tip
请使用过程替换或者for循环,而不是管道导向while循环。在while循环中被修改的变量是不能传递给父shell的,因为循环命令时在一个子shell中运行个的。

例如

# 推荐
while read count filename; do
  total+="${count}"
  last_file="${filename}"
done << (your_command | uniq -c)

# 不推荐
last_line='NULL'
your_command | while read line; do
  last_line="${line}"
done

# 会输出NULL,因为管道命令 last_line="${line}"
# 是在子shell中进行的,而父shell中last_line变量还是NULL
echo "${last_line}"

#

命名约定

函数名

❕Tip
使用小写字母,并用下划线分割单词。使用双冒号::分割库。函数名之后必须有圆括号。关键词function是可选的,但必须在一个项目中保持一致

当函数名后存在( )时,关键词function是多余的。但是其促进了函数的快速辨识。

变量名

❕Tip
如函数名。

常量和环境变量名

❕Tip
全部大写,用下划线分割,声明在文件的顶部。

源文件名

❕Tip
小写,如果需要的话使用下划线分割单词。

注意这里,源文件名是采用小写而不是大写

使用本地变量

❕Tip
使用local声明特定功能的变量。声明和赋值应该在不同行

例如

my_func2(){
  local name="$1"
  
  # 声明和赋值应该在不同行
  local my_var
  my_var="${my_func}" || return

  # 不推荐这种
  # 当赋值的值由命令替换时,声明和赋值必须分开。因为内建的 local 不会从命令替换中传递退出码
  local my_var="${my_func}"
}

调用命令

检查返回值

❕Tip
总是检查返回值,并给出信息返回值

对于非管道命令,使用$?或直接通过一个if语句来检查保持其简洁

例如

if ! mv "${file_list}" "${dest_dir}/" ; then
  echo "无法移动 ${file_list}${dest_dir}" >&2
  exit "${E_BAD_MOVE}"
fi

# 或者
mv "${file_list}" "${dest_dir}/"
if [[ "$?" -ne 0 ]]; then  # 命令执行成功正常退出码为0。 -ne 不等于
  echo "无法移动 ${file_list}${dest_dir}" >&2
  exit "${E_BAD_MOVE}"
fi

终于抄完作业了!

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐