Bash开发专家Skill bash-dev

提供Bash脚本开发的最佳实践和安全标准,包括错误处理、变量处理、函数最佳实践等,以提高脚本的健壮性和可维护性。

DevOps 0 次安装 0 次浏览 更新于 3/3/2026

Bash开发专家

这个技能支持Bash脚本开发的最佳实践和安全标准。

🎯 核心规则

Shebang和安全性

  • Shebang: 总是使用#!/usr/bin/env bash
  • 设置选项: 总是使用set -euo pipefail
    • -e: 出错时退出
    • -u: 未定义变量时退出
    • -o pipefail: 如果任何命令失败,则管道失败

变量处理

  • 引用: 总是引用变量"${var}"
  • 常量: 使用大写字母表示全局/环境变量
  • 局部变量: 使用小写字母表示函数局部变量
  • 只读: 使用readonly表示常量

函数最佳实践

  • 局部变量: 总是使用local关键字
  • 参数验证: 验证必需的参数
  • 返回代码: 成功为0,错误为非零
  • 文档: 文档使用情况、描述和返回代码

📚 脚本模板

标准脚本结构

#!/usr/bin/env bash
set -euo pipefail

# 脚本元数据
# 使用: script_name.sh <arg1> [arg2]
# 描述: 这个脚本的作用
# 作者: 你的名字
# 版本: 1.0.0

# 全局常量(大写)
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
readonly VERSION="1.0.0"

# 输出的颜色代码
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # 无颜色

#######################################
# 显示使用信息
# 全局:
#   SCRIPT_NAME
# 参数:
#   无
# 输出:
#   将使用信息写入stdout
#######################################
usage() {
  cat <<EOF
使用方式: ${SCRIPT_NAME} <arg1> [arg2]

描述:
  这个脚本详细做什么

参数:
  arg1    必需参数描述
  arg2    可选参数描述(默认: 值)

选项:
  -h, --help    显示此帮助信息
  -v, --version 显示版本信息

示例:
  ${SCRIPT_NAME} value1
  ${SCRIPT_NAME} value1 value2
EOF
}

#######################################
# 打印错误信息并退出
# 全局:
#   RED, NC
# 参数:
#   错误信息
# 输出:
#   将错误写入stderr
# 返回:
#   1(错误代码)
#######################################
error() {
  echo -e "${RED}错误: $*${NC}" >&2
  exit 1
}

#######################################
# 打印信息消息
# 全局:
#   GREEN, NC
# 参数:
#   信息消息
# 输出:
#   将消息写入stdout
#######################################
info() {
  echo -e "${GREEN}信息: $*${NC}"
}

#######################################
# 打印警告消息
# 全局:
#   YELLOW, NC
# 参数:
#   警告消息
# 输出:
#   将警告写入stdout
#######################################
warn() {
  echo -e "${YELLOW}警告: $*${NC}"
}

#######################################
# 主函数
# 全局:
#   无
# 参数:
#   命令行参数
# 返回:
#   0成功,1错误
#######################################
main() {
  local arg1="${1:?错误: arg1必需}"
  local arg2="${2:-default_value}"

  info "处理: ${arg1}"

  # 你的逻辑在这里

  info "成功完成"
  return 0
}

# 错误处理程序
trap 'error "脚本在第$LINENO行失败"' ERR

# 解析参数
case "${1:-}" in
  -h|--help)
    usage
    exit 0
    ;;
  -v|--version)
    echo "${SCRIPT_NAME} 版本 ${VERSION}"
    exit 0
    ;;
esac

# 执行主函数
main "$@"

🛠️ 常见模式

错误处理

# 检查命令是否存在
if ! command -v git &> /dev/null; then
  error "git未安装"
fi

# 检查文件是否存在
if [[ ! -f "${config_file}" ]]; then
  error "配置文件未找到: ${config_file}"
fi

# 检查目录是否存在
if [[ ! -d "${target_dir}" ]]; then
  mkdir -p "${target_dir}" || error "创建目录失败"
fi

输入验证

# 验证必需参数
validate_arg() {
  local arg="${1:?错误: 参数必需}"
  if [[ -z "${arg}" ]]; then
    error "参数不能为空"
  fi
}

# 验证数字
validate_number() {
  local num="$1"
  if ! [[ "${num}" =~ ^[0-9]+$ ]]; then
    error "无效数字: ${num}"
  fi
}

文件操作

# 安全文件读取
read_file() {
  local file="$1"
  if [[ ! -r "${file}" ]]; then
    error "无法读取文件: ${file}"
  fi
  cat "${file}"
}

# 安全文件写入
write_file() {
  local file="$1"
  local content="$2"
  echo "${content}" > "${file}" || error "写文件失败"
}

数组处理

# 声明数组
declare -a items=("item1" "item2" "item3")

# 遍历数组
for item in "${items[@]}"; do
  echo "处理: ${item}"
done

# 数组长度
echo "总项目数: ${#items[@]}"

🎯 质量检查清单

在提交前检查这些:

  • [ ] Shebang #!/usr/bin/env bash存在
  • [ ] 开头有set -euo pipefail
  • [ ] 所有变量引用"${var}"
  • [ ] 全局变量大写
  • [ ] 局部变量使用local关键字
  • [ ] 函数有文档注释
  • [ ] 实现了错误处理
  • [ ] 提供了使用函数
  • [ ] 退出代码有意义(0=成功,非零=错误)
  • [ ] 脚本用shellcheck测试过

🔍 常见反模式避免

不要:

# 未引用变量
cd $HOME/dir

# 缺少错误处理
mkdir /some/dir

# 未定义变量
echo $UNDEFINED_VAR

# 没有设置选项
#!/bin/bash

:

# 引用变量
cd "${HOME}/dir" || error "目录更改失败"

# 带错误处理
mkdir -p "${target_dir}" || error "创建目录失败"

# 使用前检查
if [[ -n "${VAR:-}" ]]; then
  echo "${VAR}"
fi

# 正确的设置选项
#!/usr/bin/env bash
set -euo pipefail

💡 测试

使用ShellCheck

# 安装shellcheck
brew install shellcheck  # macOS

# 检查脚本
shellcheck script.sh

# 忽略特定警告
# shellcheck disable=SC2086
command ${unquoted}

测试函数

# 简单测试函数
test_function() {
  local expected="expected_value"
  local actual
  actual="$(your_function)"

  if [[ "${actual}" != "${expected}" ]]; then
    error "测试失败: 预期 '${expected}', 实际 '${actual}'"
  fi
  info "测试通过"
}