Files
tools/gcp/delete_all_projects.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

207 lines
6.4 KiB
Bash
Raw 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
# ============================================================================
# 文件名: delete_all_projects.sh
# 描述: 删除 GCP 账户下的所有项目(危险操作)
# 作者: 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
# 创建日志文件
readonly LOG_FILE="deleted_projects_$(date +%Y%m%d_%H%M%S).log"
log_set_file "$LOG_FILE"
#
# 获取并展示所有项目
#
get_and_display_projects() {
# 获取所有项目
log_warning "获取项目列表..."
local projects
projects=$(gcloud projects list --format="value(projectId)")
if [ -z "$projects" ]; then
log_warning "没有找到任何项目"
exit 0
fi
# 显示所有项目
log_warning "以下项目将被删除:"
echo "$projects" | nl
echo
# 统计项目数量
local project_count
project_count=$(echo "$projects" | wc -l)
log_warning "总计: $project_count 个项目"
echo "$projects"
}
#
# 删除单个项目
#
delete_project() {
local project_id=$1
local log_file=$2
log_info "正在删除项目: $project_id"
if gcloud projects delete "$project_id" --quiet 2>&1 | tee -a "$log_file"; then
log_success "成功删除项目: $project_id"
echo "$(date): 成功删除项目 $project_id" >> "$log_file"
return 0
else
log_error "删除项目 $project_id 失败"
echo "$(date): 删除项目 $project_id 失败" >> "$log_file"
return 1
fi
}
#
# 主函数 - 删除所有 GCP 项目
#
main() {
# 第一次警告和确认
log_error "################################################################################"
log_error "警告:此脚本将删除您 GCP 账户下的所有项目!"
log_error "这是一个不可逆的操作,所有项目数据将永久丢失!"
log_error "################################################################################"
echo
# 第一次确认
read -p "您确定要继续吗?输入 'YES' 继续: " confirm1
if [ "$confirm1" != "YES" ]; then
log_warning "操作已取消"
exit 0
fi
# 获取并展示项目列表
local projects
projects=$(get_and_display_projects)
# 统计项目数量
local project_count
project_count=$(echo "$projects" | wc -l)
# 第二次确认
log_error "最后确认:您将要删除 $project_count 个项目!"
read -p "输入 'DELETE ALL' 确认删除所有项目: " confirm2
if [ "$confirm2" != "DELETE ALL" ]; then
log_warning "操作已取消"
exit 0
fi
# 开始删除项目
log_warning "开始删除项目..."
log_info "删除记录将保存到: $LOG_FILE"
echo "================== 删除开始于: $(date) ==================" >> "$LOG_FILE"
local deleted_count=0
local failed_count=0
for project_id in $projects; do
if delete_project "$project_id" "$LOG_FILE"; then
((deleted_count++))
else
((failed_count++))
fi
# 添加短暂延迟,避免 API 限制
sleep 2
done
# 显示结果
log_info "================== 执行完成 ==================="
log_success "成功删除: $deleted_count 个项目"
log_error "删除失败: $failed_count 个项目"
log_info "详细日志: $LOG_FILE"
echo "================== 删除完成于: $(date) ==================" >> "$LOG_FILE"
# 如果有删除失败的项目,返回错误状态
if [ "$failed_count" -gt 0 ]; then
log_error "$failed_count 个项目删除失败。请检查日志。"
exit 1
fi
}
# 执行主函数
main "$@"