一,背景
1.1 明确使用哪个Shell
Bash是唯一允许用于可执行文件的shell脚本语言。
可执行文件必须以 #!/bin/bash 开头,并且使用最少的标志位。使用set命令设置shell选项,以便在将脚本作为bash script_name调用时不会破坏其功能。
将所有可执行shell脚本限制为bash,使我们拥有一种在所有计算机上都安装的一致的shell语言。
唯一的例外情况是你不得不为某些你在编写代码的环境中被迫使用的情况。其中一个例子是需要使用纯Bourne shell执行脚本的Solaris SVR4软件包。
1.2 何时使用Shell
Shell仅应用于小型实用工具或简单的包装脚本。
虽然shell脚本不是一种开发语言,但在Google的各种实用脚本编写中会使用它。这个风格指南更多地是对其使用的一种承认,而不是建议广泛部署。
二,一些准则:
如果大部分时间在调用其他实用工具,并且进行的数据操作相对较少,那么shell是这项任务的可接受选择。
如果性能很重要,使用除shell之外的其他工具。
如果你正在编写的脚本超过100行,或者使用的控制流逻辑不直观,那么你应该现在就将其改写为一种更结构化的语言。请记住,脚本会逐渐增长。及时地重写你的脚本,以避免以后重写需要花费更多时间。
在评估你的代码复杂性(例如决定是否切换语言)时,请考虑代码是否易于被除了作者以外的其他人维护。
2.1 Shell 文件和解释器调用
- 文件扩展名
可执行文件应该没有扩展名(强烈推荐)或者使用 .sh 扩展名。库文件必须使用 .sh 扩展名但不可执行。
在执行程序时,不需要知道它是用哪种语言编写的,而且 shell 不要求有扩展名,所以我们更倾向于不为可执行文件使用扩展名。
然而,对于库文件来说,知道它所使用的语言很重要,并且有时候需要在不同语言中使用相似的库。这允许具有相同目的但不同语言的库文件具有相同的文件名,只是语言特定后缀不同。
- SUID/SGID
在 shell 脚本中禁止使用 SUID 和 SGID。
由于 shell 存在太多安全问题,几乎不可能通过足够的方式来保证安全,所以禁止使用 SUID/SGID。虽然 bash 在执行 SUID 方面做得比较困难,但在某些平台上仍然可能实现,这就是为什么我们明确禁止使用 SUID 的原因。
如果需要提供提升后的访问权限,请使用 sudo。
2.2 环境
- STDOUT vs STDERR
所有错误消息都应该发送到 STDERR。
这样可以更容易地区分正常状态和实际问题。
建议使用一个函数来打印错误消息和其他状态信息。
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
if ! do_something; then
err "Unable to do_something"
exit 1
fi
2.3 注释
- 文件头
每个文件都要以其内容的描述开始。
每个文件必须有一个顶级注释,其中包括对其内容的简要概述。版权声明和作者信息可选择性添加。
示例:
#!/bin/bash
#
# Perform hot backups of Oracle databases.
- 函数注释
任何不明显且不简短的函数必须进行注释。无论长度或复杂程度如何,库中的任何函数都必须进行注释。
通过阅读注释(以及提供的自助帮助,如果有的话),其他人应该能够学习如何使用您的程序或使用您库中的函数,而无需阅读代码。
所有函数注释都应描述预期的 API 行为,包括:
函数的描述。
- 全局变量:使用和修改的全局变量列表。
- 参数:接受的参数。
- 输出:输出到 STDOUT 或 STDERR。
- 返回值:除了最后一条运行命令的默认退出状态之外的返回值。
示例:
#######################################
# Cleanup files from the backup directory.
# Globals:
# BACKUP_DIR
# ORACLE_SID
# Arguments:
# None
#######################################
function cleanup() {
…
}
#######################################
# Get configuration directory.
# Globals:
# SOMEDIR
# Arguments:
# None
# Outputs:
# Writes location to stdout
#######################################
function get_dir() {
echo "${SOMEDIR}"
}
#######################################
# Delete a file in a sophisticated manner.
# Arguments:
# File to delete, a path.
# Returns:
# 0 if thing was deleted, non-zero on error.
#######################################
function del_thing() {
rm "$1"
}
- 实现注释
在代码中注释那些棘手、不明显、有趣或重要的部分。
这遵循了谷歌通常的编码注释实践。不要对每个地方都进行注释。如果有一个复杂的算法或者你正在做一些非常规的事情,请添加一个简短的注释。
- 待办事项注释
使用待办事项注释来标记临时性的代码、短期解决方案或者达到足够好但不完美的代码。
这符合 C++ Guide 中的约定。
待办事项注释应包括全大写的 TODO 字符串,后面跟着问题所涉及的最佳上下文知识人员的姓名、电子邮件地址或其他标识符。主要的目的是拥有一个一致的待办事项,可以通过搜索来找到如何在请求时获取更多详细信息。待办事项并不意味着该人将修复问题。因此,当创建一个待办事项时,几乎总是使用你自己的姓名。
# TODO(mrmonkey): Handle the unlikely edge cases (bug ####)
专栏时间
通过学习专栏,您将能够获得以下收获:
- 掌握 Shell 编程的核心概念和技巧:本专栏将深入浅出地讲解 Shell 编程的核心概念和技巧,例如 Shell 脚本的语法、变量、流程控制、函数、文件操作、正则表达式等,让您能够轻松掌握 Shell 编程的常用范式和技巧。
- 熟练使用 Shell 编程的最佳实践和规范:本专栏将介绍 Shell 编程的最佳实践和规范,例如命名规范、注释规范、代码结构规范等,让您能够编写高效、可读性强的 Shell 脚本,提高代码的可维护性和可重用性。
- 提高 Shell 脚本的效率和性能:本专栏将介绍 Shell 编程的效率和性能优化技巧,例如并行化、缓存、优化算法和数据结构等,让您的 Shell 脚本能够更加高效地运行,提升系统的整体性能。
- 提高 Shell 脚本的健壮性和稳定性:本专栏将介绍 Shell 编程的异常处理和错误处理方法,例如使用 try-catch 语句、检查错误码、备份数据等,让您的 Shell 脚本能够更加健壮、稳定,避免因为异常情况导致系统崩溃或数据丢失。
- 深入了解 Shell 编程的高级技巧和工具:本专栏将介绍 Shell 编程的高级技巧和工具,例如使用 awk、sed、grep、find 等工具进行文本处理、使用调试器进行调试、使用版本控制工具进行代码管理等,让您成为 Shell 编程的高级专家。
总之,通过学习本专栏,您将能够深入掌握 Shell 编程的核心技能和最佳实践,提高 Shell 编程的效率、可读性、健壮性和可维护性,让您在 Shell 编程领域成为技术的领军者!