feat: 代码标准化和文件重命名

## 新增功能
- 创建统一的公共函数库 (common/logging.sh, common/error_handler.sh)
- 添加功能演示脚本 (common/demo_usage.sh)
- 完善的使用文档 (common/README.md)

## 代码重构
- 重构所有脚本使用统一的公共库
- 为所有函数添加完整的文档注释
- 统一代码格式(4空格缩进、严格模式)
- 标准化错误处理和日志输出

## 文件重命名
- gcp/create_ai_project.sh → gcp/create_ai_projects.sh (单复数统一)
- gcp/delete_all_project.sh → gcp/delete_all_projects.sh (单复数统一)
- linux/install_ohmyzsh.sh → linux/install_oh_my_zsh.sh (专有名词规范)
- linux/create_raid0_with_ext4.sh → linux/create_raid0_array.sh (简化命名)
- common/example.sh → common/demo_usage.sh (更具描述性)

## 技术改进
- 使用 readonly 声明常量
- 启用 set -euo pipefail 严格模式
- 统一的 ANSI 颜色日志输出
- 完善的命令重试机制
- 栈追踪支持
This commit is contained in:
2025-12-26 14:47:18 +08:00
commit 7def817482
11 changed files with 2312 additions and 0 deletions

245
common/README.md Normal file
View File

@@ -0,0 +1,245 @@
# 公共函数库
本目录包含可复用的 Bash 函数库,用于标准化所有脚本的日志输出和错误处理。
## 📦 包含的库
### 1. logging.sh - 日志输出库
提供统一的日志输出函数,支持多个日志级别和彩色输出。
**功能特性:**
- 5个日志级别DEBUG, INFO, WARNING, ERROR, SUCCESS
- 彩色控制台输出
- 可选的时间戳
- 支持输出到文件
- 灵活的日志级别控制
**使用示例:**
```bash
#!/bin/bash
source common/logging.sh
# 基本使用
log_info "程序启动"
log_success "操作完成"
log_warning "磁盘空间不足"
log_error "连接失败"
log_debug "调试信息"
# 设置日志级别
log_set_level "DEBUG" # 显示所有日志
# 输出到文件
log_set_file "/var/log/myapp.log"
# 禁用时间戳
log_disable_timestamp
```
### 2. error_handler.sh - 错误处理库
提供统一的错误处理和检查机制。
**功能特性:**
- 命令存在性检查
- 文件/目录检查
- 变量非空检查
- Root 权限检查
- 命令返回值检查
- 带重试的命令执行
- 调用栈跟踪
**使用示例:**
```bash
#!/bin/bash
source common/error_handler.sh
# 启用严格错误处理
enable_exit_on_error
# 检查命令是否存在
check_command "git" || error_exit "git 未安装"
# 检查文件
check_file "/etc/config" || error_exit "配置文件不存在"
# 检查 root 权限
check_root || error_exit "需要 root 权限"
# 执行命令并检查结果
run_command "安装失败" apt-get install -y package
# 带重试的命令执行
retry_command 3 5 "下载失败" curl -O https://example.com/file
# 检查变量非空
check_not_empty "$PROJECT_ID" "PROJECT_ID" || error_exit "项目ID不能为空"
```
## 🚀 完整示例脚本
```bash
#!/bin/bash
# ============================================================================
# 示例脚本:展示如何使用公共函数库
# ============================================================================
# 获取脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 加载公共库
source "${SCRIPT_DIR}/../common/logging.sh"
source "${SCRIPT_DIR}/../common/error_handler.sh"
# 配置日志
log_set_level "INFO"
log_set_file "/tmp/example.log"
# 启用错误处理
enable_exit_on_error
#
# 主函数
#
main() {
log_info "脚本开始执行"
# 检查必要的命令
check_command "git" || error_exit "请先安装 git"
# 检查参数
local project_name="$1"
check_not_empty "$project_name" "project_name" || {
log_error "用法: $0 <project_name>"
exit 1
}
# 执行操作
log_info "正在处理项目: $project_name"
# 带重试的网络操作
retry_command 3 2 "网络请求失败" curl -f "https://example.com/api"
log_success "脚本执行完成"
}
# 执行主函数
main "$@"
```
## 📖 最佳实践
### 1. 脚本模板
每个脚本都应遵循以下模板结构:
```bash
#!/bin/bash
set -euo pipefail # 启用严格模式
# 脚本信息
# ============================================================================
# 文件名: script_name.sh
# 描述: 脚本功能描述
# 作者: 作者名
# 版本: 1.0.0
# ============================================================================
# 常量定义
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
# 加载公共库
source "${SCRIPT_DIR}/../common/logging.sh"
source "${SCRIPT_DIR}/../common/error_handler.sh"
# 全局变量
GLOBAL_VAR=""
#
# 显示使用帮助
#
show_usage() {
cat << EOF
使用方法: $SCRIPT_NAME [选项]
选项:
-h, --help 显示此帮助信息
-v, --verbose 启用详细输出
示例:
$SCRIPT_NAME --verbose
EOF
}
#
# 主函数
#
main() {
log_info "脚本开始执行"
# 你的代码逻辑
log_success "脚本执行完成"
}
# 执行主函数
main "$@"
```
### 2. 错误处理建议
- 总是检查命令返回值
- 使用 `check_*` 函数进行前置条件检查
- 对网络操作使用 `retry_command`
- 提供有意义的错误消息
### 3. 日志使用建议
- INFO: 正常流程信息
- SUCCESS: 操作成功完成
- WARNING: 需要注意但不影响执行的情况
- ERROR: 错误但可以继续的情况
- DEBUG: 调试信息(默认不显示)
## 🔧 环境变量配置
### logging.sh 环境变量
```bash
# 设置日志级别 (DEBUG=0, INFO=1, WARNING=2, ERROR=3, SUCCESS=4)
export LOG_CURRENT_LEVEL=1
# 设置日志文件路径
export LOG_FILE_PATH="/var/log/myapp.log"
# 启用/禁用时间戳
export LOG_ENABLE_TIMESTAMP=true
```
### error_handler.sh 环境变量
```bash
# 错误时是否退出 (true/false)
export ERROR_EXIT_ON_FAIL=true
# 是否显示调用栈 (true/false)
export ERROR_STACK_TRACE=false
```
## 📝 维护说明
- 版本: 1.0.0
- 最后更新: 2025-12-26
- 维护者: Cloud Tools Project
## 🤝 贡献
如需添加新功能或修复 bug
1. 保持向后兼容性
2. 添加完整的函数文档注释
3. 提供使用示例
4. 更新此 README

179
common/demo_usage.sh Executable file
View File

@@ -0,0 +1,179 @@
#!/bin/bash
# ============================================================================
# 文件名: demo_usage.sh
# 描述: 公共函数库功能演示和使用示例
# 作者: Cloud Tools Project
# 版本: 1.0.0
# ============================================================================
set -euo pipefail # 启用严格模式
# 获取脚本目录
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 加载公共库
source "${SCRIPT_DIR}/logging.sh"
source "${SCRIPT_DIR}/error_handler.sh"
#
# 示例 1: 基本日志使用
#
example_basic_logging() {
log_info "============ 示例 1: 基本日志使用 ============"
log_debug "这是调试信息(默认不显示)"
log_info "这是普通信息"
log_warning "这是警告信息"
log_error "这是错误信息(但不会退出)"
log_success "这是成功信息"
echo ""
}
#
# 示例 2: 日志级别控制
#
example_log_levels() {
log_info "============ 示例 2: 日志级别控制 ============"
log_info "当前日志级别: INFO"
log_debug "这条调试信息不会显示"
log_info "切换到 DEBUG 级别..."
log_set_level "DEBUG"
log_debug "现在调试信息可以显示了!"
log_info "恢复到 INFO 级别..."
log_set_level "INFO"
echo ""
}
#
# 示例 3: 错误检查
#
example_error_checking() {
log_info "============ 示例 3: 错误检查 ============"
# 临时禁用自动退出
disable_exit_on_error
# 检查命令
if check_command "bash"; then
log_success "bash 命令存在"
fi
if ! check_command "nonexistent_command"; then
log_warning "nonexistent_command 不存在(这是预期的)"
fi
# 检查文件
if check_file "${SCRIPT_DIR}/logging.sh"; then
log_success "logging.sh 文件存在"
fi
# 检查变量非空
local test_var="hello"
if check_not_empty "$test_var" "test_var"; then
log_success "test_var 变量非空"
fi
# 重新启用自动退出
enable_exit_on_error
echo ""
}
#
# 示例 4: 命令执行和重试
#
example_command_execution() {
log_info "============ 示例 4: 命令执行 ============"
# 执行简单命令
run_command "列出文件失败" ls -la "${SCRIPT_DIR}"
log_info "执行成功的命令"
# 临时禁用自动退出来演示重试
disable_exit_on_error
log_info "尝试执行一个会失败的命令(演示重试)"
retry_command 3 1 "命令失败" false
# 重新启用
enable_exit_on_error
echo ""
}
#
# 示例 5: 日志文件输出
#
example_log_file() {
log_info "============ 示例 5: 日志文件输出 ============"
local temp_log="/tmp/common_lib_example_$$.log"
log_info "设置日志输出到文件: $temp_log"
log_set_file "$temp_log"
log_info "这条消息会同时输出到控制台和文件"
log_success "日志文件功能正常"
log_info "日志文件内容:"
cat "$temp_log"
# 清理
rm -f "$temp_log"
echo ""
}
#
# 示例 6: 时间戳控制
#
example_timestamp() {
log_info "============ 示例 6: 时间戳控制 ============"
log_info "默认启用时间戳"
log_info "禁用时间戳..."
log_disable_timestamp
log_info "这条消息没有时间戳"
log_info "启用时间戳..."
log_enable_timestamp
log_info "时间戳已恢复"
echo ""
}
#
# 主函数
#
main() {
# 配置日志
log_set_level "INFO"
log_enable_timestamp
log_success "==================================================="
log_success " 公共函数库使用示例"
log_success "==================================================="
echo ""
# 执行所有示例
example_basic_logging
example_log_levels
example_error_checking
example_command_execution
example_log_file
example_timestamp
log_success "==================================================="
log_success " 所有示例执行完成!"
log_success "==================================================="
}
# 执行主函数
main "$@"

300
common/error_handler.sh Normal file
View File

@@ -0,0 +1,300 @@
#!/bin/bash
# ============================================================================
# 文件名: error_handler.sh
# 描述: 统一的错误处理机制
# 作者: Cloud Tools Project
# 版本: 1.0.0
# 依赖: logging.sh
# 使用方法: source common/error_handler.sh
# ============================================================================
# 确保加载日志库
if ! declare -f log_error >/dev/null 2>&1; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./logging.sh
source "${SCRIPT_DIR}/logging.sh"
fi
# 错误处理配置
ERROR_EXIT_ON_FAIL=${ERROR_EXIT_ON_FAIL:-true}
ERROR_STACK_TRACE=${ERROR_STACK_TRACE:-false}
#
# 内部: 获取调用栈信息
# 返回: 格式化的调用栈字符串
#
_error_get_stack_trace() {
local frame=0
local line
local func
local src
echo "调用栈:"
while caller $frame; do
((frame++))
done | while read -r line func src; do
echo " at $func ($src:$line)"
done
}
#
# 检查命令是否存在
# 参数: $1 - 命令名称
# 返回: 0=存在, 1=不存在
# 示例: check_command "git" || error_exit "git 未安装"
#
check_command() {
local cmd="$1"
if ! command -v "$cmd" &>/dev/null; then
log_error "命令不存在: $cmd"
return 1
fi
return 0
}
#
# 检查文件是否存在
# 参数: $1 - 文件路径
# 返回: 0=存在, 1=不存在
# 示例: check_file "/path/to/file" || error_exit "文件不存在"
#
check_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
log_error "文件不存在: $file"
return 1
fi
return 0
}
#
# 检查目录是否存在
# 参数: $1 - 目录路径
# 返回: 0=存在, 1=不存在
# 示例: check_directory "/path/to/dir" || error_exit "目录不存在"
#
check_directory() {
local dir="$1"
if [[ ! -d "$dir" ]]; then
log_error "目录不存在: $dir"
return 1
fi
return 0
}
#
# 检查变量是否为空
# 参数:
# $1 - 变量值
# $2 - 变量名称 (可选,用于错误消息)
# 返回: 0=非空, 1=为空
# 示例: check_not_empty "$VAR" "VAR" || error_exit "变量不能为空"
#
check_not_empty() {
local value="$1"
local var_name="${2:-变量}"
if [[ -z "$value" ]]; then
log_error "${var_name} 不能为空"
return 1
fi
return 0
}
#
# 检查是否以 root 权限运行
# 返回: 0=是root, 1=不是root
# 示例: check_root || error_exit "此脚本需要 root 权限"
#
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本需要 root 权限运行"
return 1
fi
return 0
}
#
# 检查命令返回值
# 参数:
# $1 - 命令返回值 ($?)
# $2 - 错误消息 (可选)
# 返回: 传入的返回值
# 示例: some_command; check_return $? "命令执行失败"
#
check_return() {
local ret=$1
local msg="${2:-命令执行失败}"
if [[ $ret -ne 0 ]]; then
log_error "$msg (退出码: $ret)"
if [[ "$ERROR_STACK_TRACE" == "true" ]]; then
_error_get_stack_trace >&2
fi
if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then
exit "$ret"
fi
fi
return "$ret"
}
#
# 错误退出
# 参数:
# $1 - 错误消息
# $2 - 退出码 (可选,默认: 1)
# 示例: error_exit "配置文件不存在" 2
#
error_exit() {
local msg="$1"
local exit_code="${2:-1}"
log_error "$msg"
if [[ "$ERROR_STACK_TRACE" == "true" ]]; then
_error_get_stack_trace >&2
fi
exit "$exit_code"
}
#
# 执行命令并检查结果
# 参数:
# $1 - 错误消息前缀
# $@ - 要执行的命令和参数
# 返回: 命令的返回值
# 示例: run_command "安装失败" apt-get install -y package
#
run_command() {
local error_msg="$1"
shift
log_debug "执行命令: $*"
if ! "$@"; then
local ret=$?
log_error "${error_msg}: $*"
if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then
exit "$ret"
fi
return "$ret"
fi
return 0
}
#
# 带重试的命令执行
# 参数:
# $1 - 最大重试次数
# $2 - 重试间隔 (秒)
# $3 - 错误消息前缀
# $@ - 要执行的命令和参数
# 返回: 命令的返回值
# 示例: retry_command 3 5 "连接失败" curl -f https://example.com
#
retry_command() {
local max_retries=$1
local retry_delay=$2
local error_msg="$3"
shift 3
local attempt=1
local ret
while [[ $attempt -le $max_retries ]]; do
log_debug "尝试执行 (第 $attempt/$max_retries 次): $*"
if "$@"; then
log_debug "命令执行成功"
return 0
fi
ret=$?
if [[ $attempt -lt $max_retries ]]; then
log_warning "${error_msg} (第 $attempt 次失败,将在 ${retry_delay}秒后重试)"
sleep "$retry_delay"
else
log_error "${error_msg} (已重试 $max_retries 次)"
fi
((attempt++))
done
if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then
exit "$ret"
fi
return "$ret"
}
#
# 启用错误时退出
# 示例: enable_exit_on_error
#
enable_exit_on_error() {
ERROR_EXIT_ON_FAIL=true
set -e
set -o pipefail
}
#
# 禁用错误时退出
# 示例: disable_exit_on_error
#
disable_exit_on_error() {
ERROR_EXIT_ON_FAIL=false
set +e
set +o pipefail
}
#
# 启用调用栈跟踪
# 示例: enable_stack_trace
#
enable_stack_trace() {
ERROR_STACK_TRACE=true
}
#
# 禁用调用栈跟踪
# 示例: disable_stack_trace
#
disable_stack_trace() {
ERROR_STACK_TRACE=false
}
# 设置 ERR 陷阱(可选)
# trap '_error_handler $? $LINENO' ERR
#
# 内部: ERR 陷阱处理函数
# 参数:
# $1 - 错误码
# $2 - 行号
#
_error_handler() {
local exit_code=$1
local line_number=$2
log_error "脚本在第 $line_number 行发生错误 (退出码: $exit_code)"
if [[ "$ERROR_STACK_TRACE" == "true" ]]; then
_error_get_stack_trace >&2
fi
}

199
common/logging.sh Normal file
View File

@@ -0,0 +1,199 @@
#!/bin/bash
# ============================================================================
# 文件名: logging.sh
# 描述: 统一的日志输出函数库
# 作者: Cloud Tools Project
# 版本: 1.0.0
# 依赖: 无
# 使用方法: source common/logging.sh
# ============================================================================
# 颜色定义 (ANSI 颜色代码)
readonly LOG_COLOR_RED='\033[0;31m'
readonly LOG_COLOR_GREEN='\033[0;32m'
readonly LOG_COLOR_YELLOW='\033[1;33m'
readonly LOG_COLOR_BLUE='\033[0;34m'
readonly LOG_COLOR_MAGENTA='\033[0;35m'
readonly LOG_COLOR_CYAN='\033[0;36m'
readonly LOG_COLOR_RESET='\033[0m'
# 日志级别定义
readonly LOG_LEVEL_DEBUG=0
readonly LOG_LEVEL_INFO=1
readonly LOG_LEVEL_WARNING=2
readonly LOG_LEVEL_ERROR=3
readonly LOG_LEVEL_SUCCESS=4
# 当前日志级别 (默认: INFO)
LOG_CURRENT_LEVEL=${LOG_CURRENT_LEVEL:-$LOG_LEVEL_INFO}
# 日志文件路径 (可选)
LOG_FILE_PATH="${LOG_FILE_PATH:-}"
# 是否启用时间戳
LOG_ENABLE_TIMESTAMP=${LOG_ENABLE_TIMESTAMP:-true}
#
# 获取格式化的时间戳
# 返回: 格式化的时间字符串
#
_log_get_timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
#
# 内部日志输出函数
# 参数:
# $1 - 日志级别 (DEBUG|INFO|WARNING|ERROR|SUCCESS)
# $2 - 日志颜色
# $3 - 日志消息
# $4 - 输出目标 (stdout/stderr)
#
_log_output() {
local level="$1"
local color="$2"
local message="$3"
local target="${4:-stdout}"
local timestamp=""
if [[ "$LOG_ENABLE_TIMESTAMP" == "true" ]]; then
timestamp="$(_log_get_timestamp) "
fi
local log_line="${timestamp}[${level}] ${message}"
local colored_line="${color}${log_line}${LOG_COLOR_RESET}"
# 输出到控制台
if [[ "$target" == "stderr" ]]; then
echo -e "$colored_line" >&2
else
echo -e "$colored_line"
fi
# 输出到日志文件 (如果配置了)
if [[ -n "$LOG_FILE_PATH" ]]; then
echo "$log_line" >> "$LOG_FILE_PATH"
fi
}
#
# 调试级别日志
# 参数: $1 - 日志消息
# 示例: log_debug "Debugging information"
#
log_debug() {
if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_DEBUG ]]; then
_log_output "DEBUG" "$LOG_COLOR_CYAN" "$1" "stdout"
fi
}
#
# 信息级别日志
# 参数: $1 - 日志消息
# 示例: log_info "Process started"
#
log_info() {
if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_INFO ]]; then
_log_output "INFO" "$LOG_COLOR_BLUE" "$1" "stdout"
fi
}
#
# 警告级别日志
# 参数: $1 - 日志消息
# 示例: log_warning "Disk space low"
#
log_warning() {
if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_WARNING ]]; then
_log_output "WARNING" "$LOG_COLOR_YELLOW" "$1" "stderr"
fi
}
#
# 错误级别日志
# 参数: $1 - 日志消息
# 示例: log_error "Failed to connect to server"
#
log_error() {
if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_ERROR ]]; then
_log_output "ERROR" "$LOG_COLOR_RED" "$1" "stderr"
fi
}
#
# 成功级别日志
# 参数: $1 - 日志消息
# 示例: log_success "Deployment completed"
#
log_success() {
if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_SUCCESS ]]; then
_log_output "SUCCESS" "$LOG_COLOR_GREEN" "$1" "stdout"
fi
}
#
# 设置日志级别
# 参数: $1 - 日志级别 (DEBUG|INFO|WARNING|ERROR|SUCCESS)
# 示例: log_set_level "DEBUG"
#
log_set_level() {
local level
level="$(echo "$1" | tr '[:lower:]' '[:upper:]')"
case "$level" in
DEBUG) LOG_CURRENT_LEVEL=$LOG_LEVEL_DEBUG ;;
INFO) LOG_CURRENT_LEVEL=$LOG_LEVEL_INFO ;;
WARNING) LOG_CURRENT_LEVEL=$LOG_LEVEL_WARNING ;;
ERROR) LOG_CURRENT_LEVEL=$LOG_LEVEL_ERROR ;;
SUCCESS) LOG_CURRENT_LEVEL=$LOG_LEVEL_SUCCESS ;;
*)
log_error "Invalid log level: $1"
return 1
;;
esac
}
#
# 设置日志文件路径
# 参数: $1 - 日志文件完整路径
# 示例: log_set_file "/var/log/myapp.log"
#
log_set_file() {
LOG_FILE_PATH="$1"
# 创建日志文件目录
local log_dir
log_dir="$(dirname "$LOG_FILE_PATH")"
if [[ ! -d "$log_dir" ]]; then
mkdir -p "$log_dir" 2>/dev/null || {
log_error "无法创建日志目录: $log_dir"
return 1
}
fi
# 初始化日志文件
if [[ ! -f "$LOG_FILE_PATH" ]]; then
touch "$LOG_FILE_PATH" 2>/dev/null || {
log_error "无法创建日志文件: $LOG_FILE_PATH"
return 1
}
fi
log_info "日志输出到文件: $LOG_FILE_PATH"
}
#
# 禁用时间戳
# 示例: log_disable_timestamp
#
log_disable_timestamp() {
LOG_ENABLE_TIMESTAMP=false
}
#
# 启用时间戳
# 示例: log_enable_timestamp
#
log_enable_timestamp() {
LOG_ENABLE_TIMESTAMP=true
}