Files
tools/common/demo_usage.sh
Wang Defa 89f24a7fef fix: 修复所有脚本的 process substitution 兼容性问题
## 问题描述

在使用 `set -u` 严格模式时,`source <(curl ...)` 或 `source <(wget ...)`
的 process substitution 方式会在脚本退出时产生错误:

```
/dev/fd/63: line 1: fifo: unbound variable
```

## 根本原因

Process substitution 创建的临时文件描述符(如 /dev/fd/63)在退出时
与 Bash 的 `set -u` 严格模式存在兼容性问题,导致错误消息。

## 修复方案

将 process substitution 替换为临时文件方案:

**旧方案(有问题):**
```bash
source <(curl -fsSL "$url")
```

**新方案(兼容性好):**
```bash
temp_loader=$(mktemp)
curl -fsSL "$url" -o "$temp_loader"
source "$temp_loader"
rm -f "$temp_loader"
```

## 修改的文件

批量修复了所有 7 个脚本的远程加载逻辑:

- oci/create_instance.sh
- linux/create_raid0_array.sh
- linux/install_oh_my_zsh.sh
- linux/repartition_disks.sh
- gcp/create_ai_projects.sh
- gcp/delete_all_projects.sh
- common/demo_usage.sh

## 优势

-  避免 process substitution 的兼容性问题
-  与 `set -u` 严格模式完全兼容
-  显式的临时文件管理,更易理解
-  确保所有分支都正确清理临时文件
-  保持 curl/wget 双重支持不变
2025-12-26 15:20:51 +08:00

249 lines
6.5 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ============================================================================
# 文件名: demo_usage.sh
# 描述: 公共函数库功能演示和使用示例
# 作者: Cloud Tools Project
# 版本: 2.1.0(支持远程库加载)
# ============================================================================
set -euo pipefail
# ============================================================================
# 远程库加载配置
# ============================================================================
# 远程仓库 URL可通过环境变量覆盖
readonly REMOTE_BASE_URL="${REMOTE_LIB_URL:-https://gitea.bcde.io/wangdefa/tools/raw/branch/main}"
# 获取脚本目录(用于本地加载)
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
#
# 智能加载公共库
#
# 加载策略:
# 1. 如果 FORCE_REMOTE=1强制使用远程库
# 2. 否则尝试使用本地库
# 3. 本地库不存在时自动回退到远程库
#
load_common_libs() {
local use_remote=false
# 检查是否强制远程
if [[ "${FORCE_REMOTE:-0}" == "1" ]]; then
echo "[INFO] 强制使用远程库 (FORCE_REMOTE=1)" >&2
use_remote=true
# 检查本地库是否存在
elif [[ -f "${PROJECT_ROOT}/common/logging.sh" ]] && [[ -f "${PROJECT_ROOT}/common/error_handler.sh" ]]; then
# shellcheck disable=SC1091
source "${PROJECT_ROOT}/common/logging.sh"
# shellcheck disable=SC1091
source "${PROJECT_ROOT}/common/error_handler.sh"
return 0
else
echo "[WARN] 本地库不存在,使用远程库" >&2
use_remote=true
fi
# 使用远程库
if [[ "$use_remote" == "true" ]]; then
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
local temp_loader
temp_loader=$(mktemp)
if command -v curl &>/dev/null; then
echo "[INFO] 使用 curl 下载远程库..." >&2
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
# shellcheck disable=SC1090
if source "$temp_loader"; then
rm -f "$temp_loader"
return 0
fi
fi
elif command -v wget &>/dev/null; then
echo "[INFO] 使用 wget 下载远程库..." >&2
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
# shellcheck disable=SC1090
if source "$temp_loader"; then
rm -f "$temp_loader"
return 0
fi
fi
fi
rm -f "$temp_loader"
echo "[ERROR] 无法加载公共库" >&2
echo "[ERROR] - 本地库不存在" >&2
echo "[ERROR] - 远程下载失败(需要 curl 或 wget" >&2
echo "[ERROR] - 仓库 URL: ${REMOTE_BASE_URL}" >&2
exit 1
fi
}
# 加载公共库
load_common_libs
#
# 示例 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 "$@"