Shell脚本开发规范

692 阅读2分钟

命名约定

函数名

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

  • 如果你正在写单个函数,请用小写字母来命名,并用下划线分隔单词。如果你正在写一个包,使用双冒号 :: 来分隔包名。大括号必须和函数名位于同一行(就像在Google的其他语言一样),并且函数名和圆括号之间没有空格。

# Single function
my_func() {
  ...
}

# Part of a package
mypackage::my_func() {
  ...
}

变量名

循环的变量名应该和循环的任何变量同样命名。

for zone in ${zones}; do
  something_with "${zone}"
done

常量和环境变量名

常量和任何导出到环境中的都应该大写。

readonly PATH_TO_FILES='/some/path'

# Both constant and environment
declare -xr ORACLE_SID='PROD'

第一次设置时有一些就变成了常量(例如,通过getopts)。因此,可以在getopts中或基于条件来设定常量,但之后应该立即设置其为只读。值得注意的是,在函数中 declare 不会对全局变量进行操作。所以推荐使用 readonly 和 export 来代替。

VERBOSE='false'
while getopts 'v' flag; do
  case "${flag}" in
    v) VERBOSE='true' ;;
  esac
done
readonly VERBOSE

源文件名

这是为了和在Google中的其他代码风格保持一致: maketemplate 或者 make_template ,而不是 make-template 。

只读变量

因为全局变量在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 不会从命令替换中传递退出码。

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

  ...
}

函数位置

如果你有函数,请将他们一起放在文件头部。只有includes, set 声明和常量设置可能在函数声明之前完成。不要在函数之间隐藏可执行代码。如果那样做,会使得代码在调试时难以跟踪并出现意想不到的讨厌结果。

主函数main

为了方便查找程序的开始,将主程序放入一个称为 main 的函数,作为最下面的函数。这使其和代码库的其余部分保持一致性,同时允许你定义更多变量为局部变量(如果主代码不是一个函数就不能这么做)。文件中最后的非注释行应该是对 main 函数的调用。

main "$@"

常用脚本

Shell字符串截取

格式说明
${string: start :length}从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${string: start}从 string 字符串的左边第 start 个字符开始截取,直到最后。
${string: 0-start :length}从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start}从 string 字符串的右边第 start 个字符开始截取,直到最后。
${string#*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string##*chars}从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string%*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。
${string%%*chars}从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。

${string#*chars}

url="http://c.biancheng.net/index.html"
echo ${url#*:}
结果为//c.biancheng.net/index.html。

string=abc12342341          //等号二边不要有空格 
echo ${string#a*3}     //42341  从$string左边开始,去掉最短匹配子串  
echo ${string#c*3}     //abc12342341  这样什么也没有匹配到  
echo ${string#*c1*3}   //42341  从$string左边开始,去掉最短匹配子串  
echo ${string##a*3}    //41     从$string左边开始,去掉最长匹配子串  
echo ${string%3*1}     //abc12342  从$string右边开始,去掉最短匹配子串  
echo ${string%%3*1}    //abc12     从$string右边开始,去掉最长匹配子串 

google.github.io/styleguide/

zh-google-styleguide.readthedocs.io/en/latest/c…