From 6fa49aabeeaf27bffead0d9065fec3665544ccc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E5=96=9D=E6=B0=B4=E7=9A=84=E6=9C=A8=E5=AD=90?= Date: Thu, 19 Mar 2026 14:32:48 +0800 Subject: [PATCH] Add modular install and uninstall scripts --- README.md | 114 +++++++++ install-docker.sh | 171 ++++++++++++++ install-mambaconda.sh | 274 +++++++++++++++++++++ install-nvm.sh | 215 +++++++++++++++++ install.sh | 511 ++++++++-------------------------------- uninstall-docker.sh | 114 +++++++++ uninstall-mambaconda.sh | 68 ++++++ uninstall-nvm.sh | 78 ++++++ uninstall.sh | 141 +++++++++++ 9 files changed, 1274 insertions(+), 412 deletions(-) create mode 100644 README.md create mode 100644 install-docker.sh create mode 100644 install-mambaconda.sh create mode 100644 install-nvm.sh create mode 100644 uninstall-docker.sh create mode 100644 uninstall-mambaconda.sh create mode 100644 uninstall-nvm.sh create mode 100644 uninstall.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..9948ad5 --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +# 环境安装与卸载脚本 + +这个仓库提供了一组独立的 Shell 脚本,用于安装和卸载常见开发环境工具,适合在 Linux 和 macOS 上快速初始化开发机器。 + +## 文件说明 + +- `install.sh`:总安装入口,支持交互式选择或命令行按需安装。 +- `install-docker.sh`:单独安装 Docker。 +- `install-nvm.sh`:单独安装 NVM,并自动安装 Node.js LTS。 +- `install-mambaconda.sh`:单独安装 Mambaconda,默认安装到 `$HOME/mambaconda`。 +- `uninstall.sh`:总卸载入口,支持交互式选择或命令行按需卸载。 +- `uninstall-docker.sh`:单独卸载 Docker,默认保留 Docker 数据。 +- `uninstall-nvm.sh`:单独卸载 NVM,并清理脚本追加的 Shell 初始化配置。 +- `uninstall-mambaconda.sh`:单独卸载 Mambaconda,并清理脚本追加的 Shell 初始化配置。 + +## 支持平台 + +- Linux +- macOS + +## 安装用法 + +总入口安装: + +```bash +bash install.sh +``` + +常见用法: + +```bash +bash install.sh all +bash install.sh docker +bash install.sh nvm mambaconda +``` + +也可以直接执行单独脚本: + +```bash +bash install-docker.sh +bash install-nvm.sh +bash install-mambaconda.sh +``` + +`install.sh` 支持以下模式: + +- 不带参数:交互式选择安装项 +- `all`:安装全部工具 +- `docker` / `nvm` / `mambaconda`:安装指定工具 +- 多个参数组合:安装多个指定工具 + +## 卸载用法 + +总入口卸载: + +```bash +bash uninstall.sh +``` + +常见用法: + +```bash +bash uninstall.sh all +bash uninstall.sh docker +bash uninstall.sh nvm mambaconda +``` + +也可以直接执行单独脚本: + +```bash +bash uninstall-docker.sh +bash uninstall-nvm.sh +bash uninstall-mambaconda.sh +``` + +`uninstall.sh` 支持以下模式: + +- 不带参数:交互式选择卸载项 +- `all`:卸载全部工具 +- `docker` / `nvm` / `mambaconda`:卸载指定工具 +- 多个参数组合:卸载多个指定工具 + +## 默认行为说明 + +- `install-nvm.sh` 会配置 Node.js 国内镜像和 npm 国内源。 +- `install-mambaconda.sh` 会写入 `~/.condarc`,并使用国内镜像源。 +- `install-mambaconda.sh` 默认会安装 Python 3.10。 +- `uninstall-docker.sh` 默认不会删除 Docker 数据目录。 + +如果需要在卸载 Docker 时同时删除数据,可使用: + +```bash +REMOVE_DOCKER_DATA=1 bash uninstall-docker.sh +``` + +## 安装后验证 + +```bash +docker --version +docker compose version +nvm --version && node --version && npm --version +conda --version && python --version +``` + +## 输出风格 + +现在所有入口脚本和子脚本都会使用统一的输出格式: + +- `INFO`:普通步骤提示 +- `WARN`:非阻断警告 +- `ERROR`:错误并退出 +- `DONE`:当前脚本执行完成 + +同时会显示阶段标题,便于在安装或卸载过程中快速定位当前步骤。 diff --git a/install-docker.sh b/install-docker.sh new file mode 100644 index 0000000..c4aa4c0 --- /dev/null +++ b/install-docker.sh @@ -0,0 +1,171 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +OS_TYPE="" + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +detect_system() { + case "${OSTYPE:-}" in + linux-gnu*) OS_TYPE="linux" ;; + darwin*) OS_TYPE="macos" ;; + *) error "不支持的操作系统: ${OSTYPE:-unknown},仅支持 Linux 和 macOS。" ;; + esac + info "检测到系统: $OS_TYPE" +} + +run_privileged() { + if [[ "$(id -u)" -eq 0 ]]; then + "$@" + elif command -v sudo >/dev/null 2>&1; then + sudo "$@" + else + error "该步骤需要 root 权限,请安装 sudo 或以 root 身份运行。" + fi +} + +ensure_homebrew() { + if command -v brew >/dev/null 2>&1; then + return + fi + + info "未检测到 Homebrew,开始安装..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + if [[ -x /opt/homebrew/bin/brew ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [[ -x /usr/local/bin/brew ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi +} + +install_dependencies() { + section "安装依赖" + + if [[ "$OS_TYPE" == "linux" ]]; then + if command -v apt-get >/dev/null 2>&1; then + run_privileged apt-get update + run_privileged apt-get install -y ca-certificates curl + elif command -v dnf >/dev/null 2>&1; then + run_privileged dnf install -y ca-certificates curl + elif command -v yum >/dev/null 2>&1; then + run_privileged yum install -y ca-certificates curl + else + error "当前 Linux 包管理器不受支持,请手动安装 curl 和 ca-certificates。" + fi + else + ensure_homebrew + fi +} + +install_docker_linux() { + if command -v docker >/dev/null 2>&1; then + warn "检测到 Docker CLI 已存在,跳过下载安装。" + else + local tmp_script + tmp_script="$(mktemp "/tmp/get-docker.XXXXXX.sh")" + + info "下载 Docker 官方安装脚本..." + curl -fsSL https://get.docker.com -o "$tmp_script" + run_privileged sh "$tmp_script" + rm -f "$tmp_script" + fi + + run_privileged systemctl enable docker || true + run_privileged systemctl start docker || true + + if [[ "$(id -u)" -ne 0 ]] && ! id -nG "$USER" | grep -qw docker; then + info "将用户 $USER 加入 docker 用户组..." + run_privileged usermod -aG docker "$USER" || warn "加入 docker 用户组失败。" + fi +} + +install_docker_macos() { + if brew list --cask docker >/dev/null 2>&1; then + warn "检测到 Docker Desktop 已安装,跳过安装。" + return + fi + + info "安装 Docker Desktop..." + brew install --cask docker +} + +install_docker() { + section "安装 Docker" + + if [[ "$OS_TYPE" == "linux" ]]; then + install_docker_linux + else + install_docker_macos + fi +} + +verify_installation() { + section "校验安装结果" + + if ! command -v docker >/dev/null 2>&1; then + error "安装完成后仍未找到 docker 命令。" + fi + + info "Docker 版本: $(docker --version)" + + if docker compose version >/dev/null 2>&1; then + info "Docker Compose 版本: $(docker compose version)" + else + warn "当前尚未检测到 docker compose 插件。" + fi + + if ! docker info >/dev/null 2>&1; then + warn "暂时无法连接 Docker daemon。Linux 可能需要重新登录,macOS 需要先启动 Docker Desktop。" + fi +} + +finalize() { + section "完成" + if [[ "$OS_TYPE" == "linux" ]]; then + echo "如果刚加入 docker 用户组,请重新登录后再无 sudo 使用 Docker。" + else + echo "首次使用前请先手动启动一次 Docker Desktop。" + fi + echo "可执行以下命令快速验证:" + echo " docker --version && docker compose version" + success "Docker 安装流程结束。" +} + +main() { + section "Docker 安装脚本" + detect_system + install_dependencies + install_docker + verify_installation + finalize +} + +main "$@" diff --git a/install-mambaconda.sh b/install-mambaconda.sh new file mode 100644 index 0000000..fcec66f --- /dev/null +++ b/install-mambaconda.sh @@ -0,0 +1,274 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +OS_TYPE="" +ARCH_TYPE="" +CONDA_PATH="${CONDA_PATH:-$HOME/mambaconda}" +LATEST_CONDA_VERSION="" + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +detect_system() { + section "检测系统信息" + + case "${OSTYPE:-}" in + linux-gnu*) OS_TYPE="linux" ;; + darwin*) OS_TYPE="macos" ;; + *) error "不支持的操作系统: ${OSTYPE:-unknown},仅支持 Linux 和 macOS。" ;; + esac + + case "$(uname -m)" in + x86_64|amd64) ARCH_TYPE="x86_64" ;; + arm64|aarch64) + if [[ "$OS_TYPE" == "macos" ]]; then + ARCH_TYPE="arm64" + else + ARCH_TYPE="aarch64" + fi + ;; + *) error "不支持的系统架构: $(uname -m)" ;; + esac + + info "检测到平台: $OS_TYPE $ARCH_TYPE" +} + +run_privileged() { + if [[ "$(id -u)" -eq 0 ]]; then + "$@" + elif command -v sudo >/dev/null 2>&1; then + sudo "$@" + else + error "该步骤需要 root 权限,请安装 sudo 或以 root 身份运行。" + fi +} + +ensure_homebrew() { + if command -v brew >/dev/null 2>&1; then + return + fi + + info "未检测到 Homebrew,开始安装..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + if [[ -x /opt/homebrew/bin/brew ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [[ -x /usr/local/bin/brew ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi +} + +install_dependencies() { + section "安装依赖" + + if [[ "$OS_TYPE" == "linux" ]]; then + if command -v apt-get >/dev/null 2>&1; then + run_privileged apt-get update + run_privileged apt-get install -y curl wget ca-certificates bzip2 + elif command -v dnf >/dev/null 2>&1; then + run_privileged dnf install -y curl wget ca-certificates bzip2 + elif command -v yum >/dev/null 2>&1; then + run_privileged yum install -y curl wget ca-certificates bzip2 + else + error "当前 Linux 包管理器不受支持,请手动安装 curl、wget、ca-certificates 和 bzip2。" + fi + else + ensure_homebrew + brew install curl wget + fi +} + +get_latest_version() { + section "获取版本信息" + + local api_url="https://api.github.com/repos/conda-forge/miniforge/releases/latest" + LATEST_CONDA_VERSION="$(curl -fsSL "$api_url" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1)" + + if [[ -z "$LATEST_CONDA_VERSION" ]]; then + error "无法获取最新的 Miniforge 版本。" + fi + + info "最新 Miniforge 版本: $LATEST_CONDA_VERSION" +} + +append_block_if_missing() { + local file="$1" + local marker="$2" + local block="$3" + + touch "$file" + if ! grep -qF "$marker" "$file"; then + printf '\n%s\n' "$block" >> "$file" + fi +} + +configure_shells() { + section "配置 Shell 环境" + + local bash_block zsh_block + + bash_block=$(cat <>> mambaconda initialize >>> +__conda_setup="\$('$CONDA_PATH/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" +if [ \$? -eq 0 ]; then + eval "\$__conda_setup" +else + if [ -f "$CONDA_PATH/etc/profile.d/conda.sh" ]; then + . "$CONDA_PATH/etc/profile.d/conda.sh" + else + export PATH="$CONDA_PATH/bin:\$PATH" + fi +fi +unset __conda_setup +# <<< mambaconda initialize <<< +EOF +) + + zsh_block=$(cat <>> mambaconda initialize >>> +__conda_setup="\$('$CONDA_PATH/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)" +if [ \$? -eq 0 ]; then + eval "\$__conda_setup" +else + if [ -f "$CONDA_PATH/etc/profile.d/conda.sh" ]; then + . "$CONDA_PATH/etc/profile.d/conda.sh" + else + export PATH="$CONDA_PATH/bin:\$PATH" + fi +fi +unset __conda_setup +# <<< mambaconda initialize <<< +EOF +) + + append_block_if_missing "$HOME/.bashrc" "# >>> mambaconda initialize >>>" "$bash_block" + append_block_if_missing "$HOME/.zshrc" "# >>> mambaconda initialize >>>" "$zsh_block" + + export PATH="$CONDA_PATH/bin:$PATH" +} + +write_condarc() { + section "写入 Conda 配置" + info "生成 ~/.condarc 并写入镜像配置..." + + cat > "$HOME/.condarc" <<'EOF' +channels: + - conda-forge + - defaults +show_channel_urls: true +channel_priority: flexible +default_channels: + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2 +custom_channels: + conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud + bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud + menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud + msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud + pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud +EOF +} + +install_mambaconda() { + section "安装 Mambaconda" + info "安装目录: $CONDA_PATH" + + if [[ -x "$CONDA_PATH/bin/conda" ]]; then + warn "检测到已有 Conda 安装,跳过下载安装。" + else + local package_name="" + local tmp_script="" + local github_url="" + local mirror_url="" + + if [[ "$OS_TYPE" == "linux" ]]; then + package_name="Miniforge3-${LATEST_CONDA_VERSION}-Linux-${ARCH_TYPE}.sh" + else + package_name="Miniforge3-${LATEST_CONDA_VERSION}-MacOSX-${ARCH_TYPE}.sh" + fi + + github_url="https://github.com/conda-forge/miniforge/releases/download/${LATEST_CONDA_VERSION}/${package_name}" + mirror_url="https://mirrors.ustc.edu.cn/github-release/conda-forge/miniforge/${LATEST_CONDA_VERSION}/${package_name}" + tmp_script="$(mktemp "/tmp/${package_name}.XXXXXX")" + + info "下载 Mambaconda 安装脚本..." + if ! curl -fL "$mirror_url" -o "$tmp_script"; then + warn "中科大镜像下载失败,尝试从 GitHub 下载..." + curl -fL "$github_url" -o "$tmp_script" + fi + + bash "$tmp_script" -b -p "$CONDA_PATH" + rm -f "$tmp_script" + fi + + configure_shells + write_condarc + + # shellcheck disable=SC1091 + source "$CONDA_PATH/etc/profile.d/conda.sh" + + info "安装 Python 3.10..." + conda install -y python=3.10 +} + +verify_installation() { + section "校验安装结果" + + if [[ ! -x "$CONDA_PATH/bin/conda" ]]; then + error "未在 $CONDA_PATH/bin/conda 找到 conda 可执行文件" + fi + + # shellcheck disable=SC1091 + source "$CONDA_PATH/etc/profile.d/conda.sh" + + info "Conda 版本: $(conda --version)" + info "Python 版本: $(python --version 2>&1)" +} + +finalize() { + section "完成" + echo "下一步可执行:" + echo " source \"$HOME/.bashrc\" # 或 source \"$HOME/.zshrc\"" + echo "可执行以下命令快速验证:" + echo " conda --version && python --version" + success "Mambaconda 安装流程结束。" +} + +main() { + section "Mambaconda 安装脚本" + detect_system + install_dependencies + get_latest_version + install_mambaconda + verify_installation + finalize +} + +main "$@" diff --git a/install-nvm.sh b/install-nvm.sh new file mode 100644 index 0000000..565ca1e --- /dev/null +++ b/install-nvm.sh @@ -0,0 +1,215 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +OS_TYPE="" +LATEST_NVM_VERSION="" + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +detect_system() { + case "${OSTYPE:-}" in + linux-gnu*) OS_TYPE="linux" ;; + darwin*) OS_TYPE="macos" ;; + *) error "不支持的操作系统: ${OSTYPE:-unknown},仅支持 Linux 和 macOS。" ;; + esac + info "检测到系统: $OS_TYPE" +} + +run_privileged() { + if [[ "$(id -u)" -eq 0 ]]; then + "$@" + elif command -v sudo >/dev/null 2>&1; then + sudo "$@" + else + error "该步骤需要 root 权限,请安装 sudo 或以 root 身份运行。" + fi +} + +ensure_homebrew() { + if command -v brew >/dev/null 2>&1; then + return + fi + + info "未检测到 Homebrew,开始安装..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + if [[ -x /opt/homebrew/bin/brew ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [[ -x /usr/local/bin/brew ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi +} + +install_dependencies() { + section "安装依赖" + + if [[ "$OS_TYPE" == "linux" ]]; then + if command -v apt-get >/dev/null 2>&1; then + run_privileged apt-get update + run_privileged apt-get install -y curl ca-certificates + elif command -v dnf >/dev/null 2>&1; then + run_privileged dnf install -y curl ca-certificates + elif command -v yum >/dev/null 2>&1; then + run_privileged yum install -y curl ca-certificates + else + error "当前 Linux 包管理器不受支持,请手动安装 curl 和 ca-certificates。" + fi + else + ensure_homebrew + brew install curl + fi +} + +get_latest_version() { + section "获取版本信息" + + local api_url="https://api.github.com/repos/nvm-sh/nvm/releases/latest" + LATEST_NVM_VERSION="$(curl -fsSL "$api_url" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1)" + + if [[ -z "$LATEST_NVM_VERSION" ]]; then + LATEST_NVM_VERSION="v0.39.7" + warn "获取最新版本失败,回退到 $LATEST_NVM_VERSION" + fi + + info "使用的 NVM 版本: $LATEST_NVM_VERSION" +} + +append_block_if_missing() { + local file="$1" + local marker="$2" + local block="$3" + + touch "$file" + if ! grep -qF "$marker" "$file"; then + printf '\n%s\n' "$block" >> "$file" + fi +} + +append_line_if_missing() { + local file="$1" + local line="$2" + + touch "$file" + if ! grep -qxF "$line" "$file"; then + printf '%s\n' "$line" >> "$file" + fi +} + +configure_shells() { + section "配置 Shell 环境" + + local init_block + init_block=$(cat <<'EOF' +# >>> nvm initialize >>> +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" +[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" +export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node" +# <<< nvm initialize <<< +EOF +) + + append_block_if_missing "$HOME/.bashrc" "# >>> nvm initialize >>>" "$init_block" + append_block_if_missing "$HOME/.zshrc" "# >>> nvm initialize >>>" "$init_block" + append_line_if_missing "$HOME/.profile" 'export NVM_DIR="$HOME/.nvm"' + + export NVM_DIR="$HOME/.nvm" + export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node" +} + +install_nvm() { + section "安装 NVM" + + if [[ -s "$HOME/.nvm/nvm.sh" ]]; then + warn "检测到 NVM 已存在,跳过下载安装。" + else + local primary_url="https://raw.githubusercontent.com/nvm-sh/nvm/${LATEST_NVM_VERSION}/install.sh" + local mirror_url="https://cdn.jsdelivr.net/gh/nvm-sh/nvm@${LATEST_NVM_VERSION}/install.sh" + local tmp_script + + tmp_script="$(mktemp "/tmp/install_nvm.XXXXXX.sh")" + + info "下载 NVM 安装脚本..." + if ! curl -fL "$mirror_url" -o "$tmp_script"; then + warn "镜像下载失败,尝试从 GitHub 下载..." + curl -fL "$primary_url" -o "$tmp_script" + fi + + bash "$tmp_script" + rm -f "$tmp_script" + fi + + configure_shells + + # shellcheck disable=SC1091 + source "$HOME/.nvm/nvm.sh" + + info "安装最新 LTS 版本 Node.js..." + nvm install --lts + nvm alias default 'lts/*' + + npm config set registry https://registry.npmmirror.com/ +} + +verify_installation() { + section "校验安装结果" + + if [[ ! -s "$HOME/.nvm/nvm.sh" ]]; then + error "在 $HOME/.nvm 下未找到 nvm.sh" + fi + + # shellcheck disable=SC1091 + source "$HOME/.nvm/nvm.sh" + + info "NVM 版本: $(nvm --version)" + info "Node 版本: $(node --version)" + info "npm 源: $(npm config get registry)" +} + +finalize() { + section "完成" + echo "下一步可执行:" + echo " source \"$HOME/.bashrc\" # 或 source \"$HOME/.zshrc\"" + echo "可执行以下命令快速验证:" + echo " nvm --version && node --version && npm --version" + success "NVM 安装流程结束。" +} + +main() { + section "NVM 安装脚本" + detect_system + install_dependencies + get_latest_version + install_nvm + verify_installation + finalize +} + +main "$@" diff --git a/install.sh b/install.sh index 5dfe085..8a98dee 100644 --- a/install.sh +++ b/install.sh @@ -1,454 +1,141 @@ #!/bin/bash set -euo pipefail -# 定义颜色常量,用于输出提示 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' -NC='\033[0m' # No Color +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' -# 全局变量 - 自动识别系统信息 -OS_TYPE="" -ARCH_TYPE="" -CONDA_PATH="$HOME/mambaconda" -# 稳定版版本号(动态获取) -LATEST_CONDA_VERSION="" -LATEST_NVM_VERSION="" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# 打印信息函数 info() { echo -e "${GREEN}[INFO]${NC} $1" } -# 打印警告函数 warn() { echo -e "${YELLOW}[WARN]${NC} $1" } -# 打印错误函数 +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + error() { echo -e "${RED}[ERROR]${NC} $1" exit 1 } -# 检查系统类型和架构 -detect_system() { - info "开始检测系统信息..." - - # 检测操作系统类型 - if [[ "$OSTYPE" == "linux-gnu"* ]]; then - OS_TYPE="linux" - elif [[ "$OSTYPE" == "darwin"* ]]; then - OS_TYPE="macos" - else - error "不支持的操作系统:$OSTYPE,仅支持 Linux 和 macOS" - fi - - # 检测系统架构 - ARCH=$(uname -m) - if [[ "$ARCH" == "x86_64" || "$ARCH" == "amd64" ]]; then - ARCH_TYPE="x86_64" - elif [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then - ARCH_TYPE="aarch64" - else - error "不支持的系统架构:$ARCH,仅支持 x86_64/amd64 和 aarch64/arm64" - fi - - info "系统检测完成:$OS_TYPE $ARCH_TYPE" +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" } -# 获取最新稳定版版本号 -get_stable_versions() { - info "开始获取各工具最新稳定版版本号..." - - # 获取 Mambaconda 最新稳定版(跳过 pre-release) - info "获取 Mambaconda 最新稳定版..." - LATEST_CONDA_INFO=$(curl -s https://api.github.com/repos/conda-forge/miniforge/releases/latest) - LATEST_CONDA_VERSION=$(echo "$LATEST_CONDA_INFO" | grep -Po '"tag_name": "\K.*?(?=")') - if [ -z "$LATEST_CONDA_VERSION" ]; then - error "无法获取 Mambaconda 最新稳定版版本号,请检查网络" - fi - info "Mambaconda 最新稳定版:$LATEST_CONDA_VERSION" - - # 获取 NVM 最新稳定版(跳过 pre-release) - info "获取 NVM 最新稳定版..." - LATEST_NVM_INFO=$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest) - LATEST_NVM_VERSION=$(echo "$LATEST_NVM_INFO" | grep -Po '"tag_name": "\K.*?(?=")') - if [ -z "$LATEST_NVM_VERSION" ]; then - # 备用:使用已知稳定版 - LATEST_NVM_VERSION="v0.39.7" - warn "无法获取 NVM 最新稳定版,使用备用稳定版:$LATEST_NVM_VERSION" - fi - info "NVM 最新稳定版:$LATEST_NVM_VERSION" -} - -# 检查是否为 root 用户(macOS 不建议 root) -check_root() { - if [ "$(id -u)" -eq 0 ]; then - if [[ "$OS_TYPE" == "macos" ]]; then - error "macOS 系统禁止使用 root 用户运行此脚本,请使用普通用户" - else - warn "当前为 root 用户,将安装到 /root/mambaconda 目录" - fi - fi -} - -# 安装系统依赖工具 -install_dependencies() { - info "开始安装基础依赖工具..." - - if [[ "$OS_TYPE" == "linux" ]]; then - if command -v apt &> /dev/null; then - apt update && apt install -y curl wget git ca-certificates --no-install-recommends - elif command -v yum &> /dev/null; then - yum install -y curl wget git ca-certificates - elif command -v dnf &> /dev/null; then - dnf install -y curl wget git ca-certificates - else - error "Linux 系统不支持的包管理器,请手动安装 curl wget git" - fi - elif [[ "$OS_TYPE" == "macos" ]]; then - # 检查是否安装 brew - if ! command -v brew &> /dev/null; then - warn "未检测到 Homebrew,正在自动安装..." - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - fi - brew install curl wget git - fi - - info "基础依赖工具安装完成" -} - -# 安装 mambaconda 并配置清华源(仅稳定版) -install_mambaconda() { - info "开始安装 Mambaconda(稳定版 $LATEST_CONDA_VERSION)..." - # 检查是否已安装 conda - if command -v conda &> /dev/null; then - warn "检测到已安装 conda,跳过安装步骤" - configure_conda_env - return - fi - # Miniforge3-26.1.0-0-Linux-x86_64.sh - # 构建安装包名称 - if [[ "$OS_TYPE" == "linux" ]]; then - MAMBAFORGE_PACKAGE="Miniforge3-${LATEST_CONDA_VERSION}-Linux-${ARCH_TYPE}.sh" - elif [[ "$OS_TYPE" == "macos" ]]; then - if [[ "$ARCH_TYPE" == "x86_64" ]]; then - MAMBAFORGE_PACKAGE="Miniforge3-${LATEST_CONDA_VERSION}-MacOSX-x86_64.sh" - else - MAMBAFORGE_PACKAGE="Miniforge3-${LATEST_CONDA_VERSION}-MacOSX-arm64.sh" - fi - fi - # https://github.com/conda-forge/miniforge/releases/download/26.1.0-0/Miniforge3-26.1.0-0-Linux-x86_64.sh - # 构建下载 URL(仅稳定版) - GITHUB_URL="https://github.com/conda-forge/miniforge/releases/download/${LATEST_CONDA_VERSION}/${MAMBAFORGE_PACKAGE}" - USTC_URL="https://mirrors.ustc.edu.cn/github-release/conda-forge/miniforge/${LATEST_CONDA_VERSION}/${MAMBAFORGE_PACKAGE}" - BACKUP_URL="https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniforge3-Linux-${ARCH_TYPE}.sh" - - # 下载安装包(使用中科大镜像源) - info "下载 Mambaconda 安装包(中科大源)..." - if ! wget -q "$USTC_URL" -O "/tmp/$MAMBAFORGE_PACKAGE"; then - info "中科大源下载失败,尝试 GitHub 官方源..." - if ! wget -q "$GITHUB_URL" -O "/tmp/$MAMBAFORGE_PACKAGE"; then - info "GitHub 源下载失败,尝试备用源..." - if ! wget -q "$BACKUP_URL" -O "/tmp/$MAMBAFORGE_PACKAGE"; then - error "所有源下载 mambaconda 安装包均失败,请手动下载稳定版安装包到 /tmp 目录后重新运行" - fi - fi - fi - - # 静默安装(不修改 shell 配置文件) - bash "/tmp/$MAMBAFORGE_PACKAGE" -b -p "$CONDA_PATH" || error "mambaconda 安装失败" - - # 删除安装包 - rm -f "/tmp/$MAMBAFORGE_PACKAGE" - - # 配置 conda 环境变量 - configure_conda_env - - # 激活 conda - source "$CONDA_PATH/etc/profile.d/conda.sh" - source "$CONDA_PATH/etc/profile.d/mamba.sh" - - # 配置清华源 - info "配置 conda 清华源..." - cat > "$HOME/.condarc" << EOF -channels: - - defaults -show_channel_urls: true -default_channels: - - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main - - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r - - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2 -custom_channels: - conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud - msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud - bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud - menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud - pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud - pytorch-lts: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud - simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud +usage() { + cat <<'EOF' +用法: + bash install.sh # 交互式选择安装项 + bash install.sh all # 安装全部工具 + bash install.sh docker # 仅安装 Docker + bash install.sh nvm # 仅安装 NVM + bash install.sh mambaconda # 仅安装 Mambaconda + bash install.sh docker nvm # 安装多个指定工具 + bash install.sh --help EOF - - # 更新 conda 并安装 Python 3.10 - info "安装 Python 3.10..." - conda install -y python=3.10 || error "Python 3.10 安装失败" - - # 初始化 conda 到 shell - conda init bash - if command -v zsh &> /dev/null; then - conda init zsh - fi - if [[ "$OS_TYPE" == "macos" ]] && command -v fish &> /dev/null; then - conda init fish - fi - - info "Mambaconda 稳定版安装并配置完成" } -# 配置 conda 环境变量 -configure_conda_env() { - info "配置 conda 环境变量..." - # 检查 .bashrc 中是否已有 conda 配置 - if ! grep -q "conda initialize" "$HOME/.bashrc"; then - cat >> "$HOME/.bashrc" << EOF +run_script() { + local script_name="$1" + local script_path="$SCRIPT_DIR/$script_name" -# >>> conda initialize >>> -__conda_setup="\$('$CONDA_PATH/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" -if [ \$? -eq 0 ]; then - eval "\$__conda_setup" -else - if [ -f "$CONDA_PATH/etc/profile.d/conda.sh" ]; then - . "$CONDA_PATH/etc/profile.d/conda.sh" - else - export PATH="$CONDA_PATH/bin:\$PATH" - fi -fi -unset __conda_setup -# <<< conda initialize <<< -EOF + if [[ ! -f "$script_path" ]]; then + error "未找到脚本: $script_path" fi - # 配置 zsh(如果存在) - if command -v zsh &> /dev/null && ! grep -q "conda initialize" "$HOME/.zshrc"; then - cat >> "$HOME/.zshrc" << EOF - -# >>> conda initialize >>> -__conda_setup="\$('$CONDA_PATH/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)" -if [ \$? -eq 0 ]; then - eval "\$__conda_setup" -else - if [ -f "$CONDA_PATH/etc/profile.d/conda.sh" ]; then - . "$CONDA_PATH/etc/profile.d/conda.sh" - else - export PATH="$CONDA_PATH/bin:\$PATH" - fi -fi -unset __conda_setup -# <<< conda initialize <<< -EOF - fi - - # 立即生效环境变量 - export PATH="$CONDA_PATH/bin:$PATH" - info "conda 环境变量配置完成" + section "执行 $script_name" + bash "$script_path" +} + +install_docker() { + run_script "install-docker.sh" } -# 安装 nvm 并配置淘宝源(仅稳定版) install_nvm() { - info "开始安装 NVM(稳定版 $LATEST_NVM_VERSION)..." - # 检查是否已安装 nvm - if [ -d "$HOME/.nvm" ]; then - warn "检测到已安装 nvm,跳过安装步骤" - configure_nvm_env + run_script "install-nvm.sh" +} + +install_mambaconda() { + run_script "install-mambaconda.sh" +} + +install_targets() { + local target + + for target in "$@"; do + case "$target" in + docker) install_docker ;; + nvm) install_nvm ;; + mambaconda) install_mambaconda ;; + all) + install_docker + install_nvm + install_mambaconda + ;; + *) + error "未知安装目标: $target" + ;; + esac + done +} + +interactive_install() { + local reply="" + local selected=() + + section "交互式安装" + + read -r -p "是否安装 Docker? [y/N]: " reply + if [[ "$reply" =~ ^[Yy]$ ]]; then + selected+=("docker") + fi + + read -r -p "是否安装 NVM? [y/N]: " reply + if [[ "$reply" =~ ^[Yy]$ ]]; then + selected+=("nvm") + fi + + read -r -p "是否安装 Mambaconda? [y/N]: " reply + if [[ "$reply" =~ ^[Yy]$ ]]; then + selected+=("mambaconda") + fi + + if [[ ${#selected[@]} -eq 0 ]]; then + warn "未选择任何安装项。" return fi - # 构建 nvm 安装脚本 URL(仅稳定版) - NVM_INSTALL_URL="https://raw.githubusercontent.com/nvm-sh/nvm/${LATEST_NVM_VERSION}/install.sh" - NVM_MIRROR_URL="https://cdn.jsdelivr.net/gh/nvm-sh/nvm@${LATEST_NVM_VERSION}/install.sh" - - # 下载并安装 nvm(使用国内镜像源) - info "下载 NVM 安装脚本(稳定版)..." - if ! wget -q "$NVM_MIRROR_URL" -O "/tmp/install_nvm.sh"; then - info "镜像源下载失败,尝试 GitHub 官方源..." - if ! wget -q "$NVM_INSTALL_URL" -O "/tmp/install_nvm.sh"; then - error "下载 NVM 稳定版安装脚本失败,请检查网络" - fi - fi - - bash "/tmp/install_nvm.sh" || error "nvm 安装失败" - - # 删除安装包 - rm -f "/tmp/install_nvm.sh" - - # 配置 nvm 环境变量 - configure_nvm_env - - # 激活 nvm - export NVM_DIR="$HOME/.nvm" - if [ -s "$NVM_DIR/nvm.sh" ]; then - . "$NVM_DIR/nvm.sh" - fi - if [ -s "$NVM_DIR/bash_completion" ]; then - . "$NVM_DIR/bash_completion" - fi - - # 配置 nvm 镜像源(淘宝源) - info "配置 nvm 淘宝镜像源..." - echo 'export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node"' >> "$HOME/.bashrc" - if command -v zsh &> /dev/null; then - echo 'export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node"' >> "$HOME/.zshrc" - fi - export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node" - - # 安装 Node LTS 稳定版 - info "安装 Node LTS 稳定版..." - # 直接安装 LTS 稳定版,无需手动解析版本号 - nvm install --lts || error "Node LTS 版本安装失败" - # 设置默认版本为 LTS - nvm alias default lts/* || error "设置 Node 默认版本失败" - - # 配置 npm 淘宝源 - info "配置 npm 淘宝源..." - npm config set registry https://registry.npmmirror.com/ || error "配置 npm 源失败" - # 持久化 npm 配置(全局生效) - npm config set registry https://registry.npmmirror.com/ --global - - info "NVM 稳定版安装并配置完成" - # # 安装 Node LTS 稳定版(跳过 pre-release) - # info "安装 Node LTS 稳定版..." - # # 获取 Node LTS 稳定版版本号(跳过 pre) - # NODE_LTS_VERSION=$(nvm ls-remote --lts | grep -v "-rc" | grep -v "-beta" | tail -1 | awk '{print $1}') - # if [ -z "$NODE_LTS_VERSION" ]; then - # # 备用:直接安装 lts - # nvm install --lts || error "Node LTS 版本安装失败" - # else - # nvm install "$NODE_LTS_VERSION" || error "Node LTS 版本安装失败" - # fi - # nvm alias default lts/* || error "设置 Node 默认版本失败" - - # # 配置 npm 淘宝源 - # info "配置 npm 淘宝源..." - # npm config set registry https://registry.npmmirror.com/ || error "配置 npm 源失败" - # # 持久化 npm 配置 - # npm config set registry https://registry.npmmirror.com/ --global - - # info "NVM 稳定版安装并配置完成" + install_targets "${selected[@]}" + success "所选安装项已执行完成。" } -# 配置 nvm 环境变量 -configure_nvm_env() { - info "配置 nvm 环境变量..." - # 检查 .bashrc 中是否已有 nvm 配置 - if ! grep -q "NVM_DIR" "$HOME/.bashrc"; then - cat >> "$HOME/.bashrc" << EOF - -# >>> nvm initialize >>> -export NVM_DIR="$HOME/.nvm" -[ -s "\$NVM_DIR/nvm.sh" ] && \. "\$NVM_DIR/nvm.sh" -[ -s "\$NVM_DIR/bash_completion" ] && \. "\$NVM_DIR/bash_completion" -# <<< nvm initialize <<< -EOF - fi - - # 配置 zsh(如果存在) - if command -v zsh &> /dev/null && ! grep -q "NVM_DIR" "$HOME/.zshrc"; then - cat >> "$HOME/.zshrc" << EOF - -# >>> nvm initialize >>> -export NVM_DIR="$HOME/.nvm" -[ -s "\$NVM_DIR/nvm.sh" ] && \. "\$NVM_DIR/nvm.sh" -[ -s "\$NVM_DIR/bash_completion" ] && \. "\$NVM_DIR/bash_completion" -# <<< nvm initialize <<< -EOF - fi - - # 立即生效环境变量 - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - info "nvm 环境变量配置完成" -} - -# 验证安装结果 -verify_installation() { - info "开始验证安装结果..." - - # 临时关闭未定义变量检查,避免 PS1 报错 - set +u - # 重新加载环境变量 - source "$HOME/.bashrc" - # 恢复严格模式 - set -u - - source "$CONDA_PATH/etc/profile.d/conda.sh" - - # 验证 conda - if command -v conda &> /dev/null; then - info "✅ Mambaconda 安装成功:$(conda --version)" - info "✅ Python 版本:$(python --version)" - else - warn "❌ Mambaconda 验证失败,请手动执行 source $CONDA_PATH/etc/profile.d/conda.sh" - fi - - # 验证 nvm/node - if command -v nvm &> /dev/null; then - info "✅ NVM 安装成功:$(nvm --version)" - info "✅ Node 版本:$(node --version)" - info "✅ NPM 源:$(npm config get registry)" - else - warn "❌ NVM 验证失败,请手动执行 source $HOME/.nvm/nvm.sh" - fi -} - -# 清理和提示 -finalize() { - info "========== 安装配置全部完成 ==========" - info "已安装的稳定版:" - info " - Mambaconda: $LATEST_CONDA_VERSION (Python 3.10)" - info " - NVM: $LATEST_NVM_VERSION (Node LTS 稳定版)" - info "请执行以下命令使配置立即生效:" - - if [[ "$OS_TYPE" == "linux" ]]; then - echo "source $HOME/.bashrc" - elif [[ "$OS_TYPE" == "macos" ]]; then - echo "source $HOME/.zshrc" - fi - - info "验证安装的命令:" - echo "conda --version && python --version" - echo "nvm --version && node --version && npm --version" - echo "npm config get registry && conda config --show-sources" -} - -# 主执行流程 main() { - info "========== 开始自动安装稳定版 mambaconda 和 nvm ==========" - - # 1. 检测系统信息 - detect_system - - # 2. 检查用户权限 - check_root - - # 3. 安装基础依赖 - install_dependencies - - # 4. 获取最新稳定版版本号(跳过 pre-release) - get_stable_versions - - # 5. 安装 mambaconda 稳定版 - install_mambaconda - - # 6. 安装 nvm 稳定版 - install_nvm + section "环境安装入口" - # 7. 验证安装结果 - verify_installation + if [[ $# -eq 0 ]]; then + interactive_install + return + fi - # 8. 最终提示 - finalize + case "${1:-}" in + -h|--help) + usage + return + ;; + esac + + install_targets "$@" + success "安装流程执行完成。" } -# 执行主函数 -main \ No newline at end of file +main "$@" diff --git a/uninstall-docker.sh b/uninstall-docker.sh new file mode 100644 index 0000000..9dd7e80 --- /dev/null +++ b/uninstall-docker.sh @@ -0,0 +1,114 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +OS_TYPE="" +REMOVE_DOCKER_DATA="${REMOVE_DOCKER_DATA:-0}" + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +detect_system() { + case "${OSTYPE:-}" in + linux-gnu*) OS_TYPE="linux" ;; + darwin*) OS_TYPE="macos" ;; + *) error "不支持的操作系统: ${OSTYPE:-unknown},仅支持 Linux 和 macOS。" ;; + esac + info "检测到系统: $OS_TYPE" +} + +run_privileged() { + if [[ "$(id -u)" -eq 0 ]]; then + "$@" + elif command -v sudo >/dev/null 2>&1; then + sudo "$@" + else + error "该步骤需要 root 权限,请安装 sudo 或以 root 身份运行。" + fi +} + +uninstall_linux() { + section "卸载 Docker" + info "移除 Docker 相关软件包..." + + if command -v apt-get >/dev/null 2>&1; then + run_privileged apt-get remove -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras docker-compose || true + run_privileged rm -f /etc/apt/sources.list.d/docker.list + run_privileged rm -f /etc/apt/keyrings/docker.asc + elif command -v dnf >/dev/null 2>&1; then + run_privileged dnf remove -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras docker-compose || true + run_privileged rm -f /etc/yum.repos.d/docker-ce.repo + elif command -v yum >/dev/null 2>&1; then + run_privileged yum remove -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras docker-compose || true + run_privileged rm -f /etc/yum.repos.d/docker-ce.repo + else + warn "当前 Linux 包管理器不受支持,请手动卸载 Docker。" + fi + + if [[ "$REMOVE_DOCKER_DATA" == "1" ]]; then + warn "删除 Docker 数据目录..." + run_privileged rm -rf /var/lib/docker /var/lib/containerd + else + warn "默认保留 Docker 数据。如需删除 /var/lib/docker 和 /var/lib/containerd,请设置 REMOVE_DOCKER_DATA=1。" + fi +} + +uninstall_macos() { + section "卸载 Docker" + + if command -v brew >/dev/null 2>&1 && brew list --cask docker >/dev/null 2>&1; then + info "移除 Docker Desktop..." + brew uninstall --cask docker || true + else + warn "未检测到 Docker Desktop cask。" + fi + + if [[ "$REMOVE_DOCKER_DATA" == "1" ]]; then + warn "删除 Docker Desktop 数据..." + rm -rf "$HOME/Library/Containers/com.docker.docker" + rm -rf "$HOME/Library/Application Support/Docker Desktop" + rm -rf "$HOME/.docker" + else + warn "默认保留 Docker Desktop 数据。如需删除用户数据,请设置 REMOVE_DOCKER_DATA=1。" + fi +} + +main() { + section "Docker 卸载脚本" + detect_system + + if [[ "$OS_TYPE" == "linux" ]]; then + uninstall_linux + else + uninstall_macos + fi + + success "Docker 卸载流程结束。" +} + +main "$@" diff --git a/uninstall-mambaconda.sh b/uninstall-mambaconda.sh new file mode 100644 index 0000000..db77b4b --- /dev/null +++ b/uninstall-mambaconda.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +CONDA_PATH="${CONDA_PATH:-$HOME/mambaconda}" + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +remove_block() { + local file="$1" + local begin="$2" + local end="$3" + local tmp_file="" + + [[ -f "$file" ]] || return 0 + + tmp_file="$(mktemp)" + awk -v begin="$begin" -v end="$end" ' + index($0, begin) { skip=1; next } + index($0, end) { skip=0; next } + !skip { print } + ' "$file" > "$tmp_file" + mv "$tmp_file" "$file" +} + +main() { + section "Mambaconda 卸载脚本" + + if [[ -d "$CONDA_PATH" ]]; then + info "删除 $CONDA_PATH ..." + rm -rf "$CONDA_PATH" + else + warn "目录不存在: $CONDA_PATH" + fi + + if [[ -f "$HOME/.condarc" ]]; then + info "删除 $HOME/.condarc ..." + rm -f "$HOME/.condarc" + fi + + remove_block "$HOME/.bashrc" "# >>> mambaconda initialize >>>" "# <<< mambaconda initialize <<<" + remove_block "$HOME/.zshrc" "# >>> mambaconda initialize >>>" "# <<< mambaconda initialize <<<" + + success "Mambaconda 卸载流程结束。" +} + +main "$@" diff --git a/uninstall-nvm.sh b/uninstall-nvm.sh new file mode 100644 index 0000000..318e85e --- /dev/null +++ b/uninstall-nvm.sh @@ -0,0 +1,78 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +remove_block() { + local file="$1" + local begin="$2" + local end="$3" + local tmp_file="" + + [[ -f "$file" ]] || return 0 + + tmp_file="$(mktemp)" + awk -v begin="$begin" -v end="$end" ' + index($0, begin) { skip=1; next } + index($0, end) { skip=0; next } + !skip { print } + ' "$file" > "$tmp_file" + mv "$tmp_file" "$file" +} + +remove_line() { + local file="$1" + local line="$2" + local tmp_file="" + + [[ -f "$file" ]] || return 0 + + tmp_file="$(mktemp)" + awk -v line="$line" '$0 != line { print }' "$file" > "$tmp_file" + mv "$tmp_file" "$file" +} + +main() { + section "NVM 卸载脚本" + + if [[ -d "$HOME/.nvm" ]]; then + info "删除 $HOME/.nvm ..." + rm -rf "$HOME/.nvm" + else + warn "目录不存在: $HOME/.nvm" + fi + + remove_block "$HOME/.bashrc" "# >>> nvm initialize >>>" "# <<< nvm initialize <<<" + remove_block "$HOME/.zshrc" "# >>> nvm initialize >>>" "# <<< nvm initialize <<<" + remove_line "$HOME/.profile" 'export NVM_DIR="$HOME/.nvm"' + + if command -v npm >/dev/null 2>&1; then + npm config delete registry >/dev/null 2>&1 || true + fi + + success "NVM 卸载流程结束。" +} + +main "$@" diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 0000000..7072ae6 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,141 @@ +#!/bin/bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +success() { + echo -e "${BLUE}[DONE]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +section() { + echo + echo -e "${BOLD}${BLUE}== $1 ==${NC}" +} + +usage() { + cat <<'EOF' +用法: + bash uninstall.sh # 交互式选择卸载项 + bash uninstall.sh all # 卸载全部工具 + bash uninstall.sh docker # 仅卸载 Docker + bash uninstall.sh nvm # 仅卸载 NVM + bash uninstall.sh mambaconda # 仅卸载 Mambaconda + bash uninstall.sh docker nvm # 卸载多个指定工具 + bash uninstall.sh --help +EOF +} + +run_script() { + local script_name="$1" + local script_path="$SCRIPT_DIR/$script_name" + + if [[ ! -f "$script_path" ]]; then + error "未找到脚本: $script_path" + fi + + section "执行 $script_name" + bash "$script_path" +} + +uninstall_docker() { + run_script "uninstall-docker.sh" +} + +uninstall_nvm() { + run_script "uninstall-nvm.sh" +} + +uninstall_mambaconda() { + run_script "uninstall-mambaconda.sh" +} + +uninstall_targets() { + local target + + for target in "$@"; do + case "$target" in + docker) uninstall_docker ;; + nvm) uninstall_nvm ;; + mambaconda) uninstall_mambaconda ;; + all) + uninstall_docker + uninstall_nvm + uninstall_mambaconda + ;; + *) + error "未知卸载目标: $target" + ;; + esac + done +} + +interactive_uninstall() { + local reply="" + local selected=() + + section "交互式卸载" + + read -r -p "是否卸载 Docker? [y/N]: " reply + if [[ "$reply" =~ ^[Yy]$ ]]; then + selected+=("docker") + fi + + read -r -p "是否卸载 NVM? [y/N]: " reply + if [[ "$reply" =~ ^[Yy]$ ]]; then + selected+=("nvm") + fi + + read -r -p "是否卸载 Mambaconda? [y/N]: " reply + if [[ "$reply" =~ ^[Yy]$ ]]; then + selected+=("mambaconda") + fi + + if [[ ${#selected[@]} -eq 0 ]]; then + warn "未选择任何卸载项。" + return + fi + + uninstall_targets "${selected[@]}" + success "所选卸载项已执行完成。" +} + +main() { + section "环境卸载入口" + + if [[ $# -eq 0 ]]; then + interactive_uninstall + return + fi + + case "${1:-}" in + -h|--help) + usage + return + ;; + esac + + uninstall_targets "$@" + success "卸载流程执行完成。" +} + +main "$@"