Shell 编程规范:保证脚本可读性、可维护性、可移植性和安全性的核心准则
1.文件基础规范
(1)脚本头部(Shebang)
必须以#!/bin/bash(或 #!/bin/sh,适配 POSIX 标准)开头,明确指定解释器,避免依赖系统默认Shell
(2)文件命名与编码
命名:小写字母+下划线(蛇形命名法),后缀为 .sh(如 backup_mysql.sh),禁止使用空格、特殊字符或大写字母。命名需 “见名知意”,避免缩写
编码:统一使用 UTF-8,避免中文乱码(可通过 set fileencoding 验证)。
权限:脚本执行权限设为 755(chmod 755 script.sh),避免过度权限(如 777)。
(3)脚本说明注释(必须包含)
头部注释需说明脚本功能、作者、创建时间、参数说明、依赖等,示例:
2.语法与编码规范
(1)严格模式(推荐启用)
脚本开头启用严格模式,捕获未定义变量、命令执行失败等问题,提升健壮性:
(2)缩进与换行/空格使用
a.缩进:统一使用 4 个空格(禁止 Tab 键,避免跨编辑器显示不一致)。
b.换行:每行代码不超过 80 个字符(超长时换行,换行符后加缩进);运算符 / 管道符 | 后换行,且后续行缩进一层:
c.空行:脚本头部注释后空 1 行;函数之间空 1 行;逻辑块(如条件、循环)前后空 1 行(简短逻辑可省略)。
d.空格: 运算符两侧加空格:=、==、&&、||、+、- 等;逗号、分号后加空格;括号内侧不加空格:
(3)变量规范
命名:小写字母 + 下划线(如 backup_dir),禁止使用大写(系统环境变量通常大写,避免冲突)。
定义:变量赋值时等号两侧无空格(name="value",而非 name = "value")。
引用:所有变量引用必须加双引号("$var"),避免空格、通配符解析错误:
常量:全大写+下划线(readonly),用 readonly 或 declare -r:如 readonly MAX_BACKUP_DAYS=7
临时变量: 前缀加 tmp_ ,如:tmp_file
数组:定义或引用数组时使用规范格式,如:
# 数组定义(元素带空格时必须加引号):backup_dbs=("test_db" "prod_db" "user_db")
# 数组引用(遍历所有元素,保留空格):for db in "${backup_dbs[@]}"; do ...
(4)函数规范
命名:小写字母 + 下划线,动词开头(如 check_mysql_status)。
结构:函数必须有注释说明功能、入参、返回值,示例:
函数定义必须置顶(或在调用前),禁止先调用后定义;
函数内优先使用局部变量(local),避免污染全局变量:如函数内加local file="$1" # 局部变量,仅函数内有效
函数返回值:仅用0(成功)非0(失败),复杂结果通过 echo 输出并捕获:
调用:函数调用时参数传递清晰,避免隐式依赖全局变量。
(5)条件判断
优先使用 [[ ]](Bash 扩展)替代 [ ] 或 test,支持正则、逻辑运算符(&&/||(而非 -a/-o)),更安全:
文件判断常用运算符:
-f:是否为普通文件 -d:是否为目录
-x:是否可执行 -s:文件是否非空
多条件分支:case 语句格式规范
(6)循环规范
避免无边界循环,循环变量命名清晰:
遍历数组时使用 "${array[@]}" 避免空格分割问题
(7)输出与日志
标准输出(stdout)用于正常信息,标准错误(stderr)用于错误信息:
生产脚本建议记录日志,而非直接输出到终端:
(8)重定向与管道
错误输出(stderr)统一重定向到指定位置,避免混杂到标准输出。如:# 静默执行(屏蔽所有输出) /usr/bin/rm -f "$tmp_file" >/dev/null 2>&1
管道命令避免多层嵌套(超过 3 层建议拆分),提升可读性。该脚本需拆分:cat access.log | grep "POST" | awk '{print $7}' | sort | uniq -c | head -10
3.安全规范
(1)路径与命令安全
所有命令使用绝对路径(如 /usr/bin/mysql 而非 mysql),避免 PATH 劫持:
# 错误 mysql -e "backup"
# 正确 /usr/bin/mysql -e "backup"
避免使用 eval(易注入)、set -i(交互模式)等危险命令。
(2)参数校验
脚本入参必须校验,如检查参数数量、合法性:
if [[ $# -ne 2 ]]; then
echo "使用说明: $0 [数据库名] [备份目录]" >&2
exit 1
fi
db_name="$1"
backup_dir="$2"
# 校验备份目录是否存在
if [[ ! -d "$backup_dir" ]]; then
echo "ERROR: 备份目录 $backup_dir 不存在" >&2
exit 1
fi
(3)权限控制
避免以 root 运行脚本,仅在必要时使用 sudo,且限定最小权限。
临时文件使用 /tmp/[脚本名].$$($$ 为进程 ID),避免覆盖或劫持:
TMP_FILE="/tmp/backup_mysql.$$"
trap 'rm -f "$TMP_FILE"' EXIT # 脚本退出时删除临时文件
(4)避免硬编码敏感信息
密码、密钥等敏感信息不直接写在脚本中,通过环境变量或配置文件读取
4.可维护性与性能
(1)模块化:
将通用功能抽离为函数或独立脚本,避免重复代码。
(2)注释规范:
复杂逻辑必须加注释,说明 “为什么这么做”(而非 “做了什么”)。
临时注释(如调试)使用 # TODO 标记,上线前清理。
脚本头部注释:见「Shell 编程规范」的头部模板,必须包含功能、作者、使用说明等核心信息
函数注释:函数上方注释,说明功能、入参、返回值(示例见前文)
行内注释:代码右侧,# 后加 1 个空格,注释简短(不超过 50 字符)
块注释:复杂逻辑前的多行注释,每行以 # 开头,空行分隔
(3)性能优化:
避免频繁创建子进程(如循环中调用 echo/cat)。
大文件处理优先使用 awk/sed 而非 Shell 循环。
(4)兼容性:
如需适配多系统,避免使用 Bash 特有语法(如 [[ ]]),改用 POSIX 标准语法([ ])。
脚本开头可检查 Bash 版本:
if [[ ${BASH_VERSION%%.*} -lt 4 ]]; then
echo "ERROR: 需要Bash 4.0以上版本" >&2
exit 1
fi
5.调试与测试
(1)调试技巧:
开发时启用 set -x 打印执行命令,或使用 bash -x script.sh 运行脚本。
使用 trap 'echo "Error at line $LINENO"' ERR 捕获错误行号。
(2)测试场景:
测试空参数、非法参数、文件不存在、权限不足等异常场景。
生产环境上线前先在测试环境验证。
6.常见的几个错误
错误类型 错误示例 正确写法
变量未加双引号 rm -rf $dir/* rm -rf "$dir"/*
等号两侧加空格 name = "test" name="test"
使用 == 赋值 if [[ $var == "test" ]] 赋值用 =,判断用 ==(此处正确)
循环遍历空值 for f in $(ls $dir) for f in "$dir"/*
忽略命令返回值 mysql -e "backup" if ! mysql -e "backup"; then ...
浙公网安备 33010602011771号