项目文件夹

文件
2026-07-13 13:03:09 +08:00

191 行
5.3 KiB
Bash
可执行文件

#!/usr/bin/env bash
set -euo pipefail
: "${HOME:=$PWD}"
# Zsh safety: allow empty globs (re-run safe)
setopt NULL_GLOB 2>/dev/null || true
SWITCHED_TO_ZSH="${SWITCHED_TO_ZSH:-0}"
# =========================================================
# FORCE ZSH ON MACOS (Finder-friendly)
# =========================================================
if [[ "${OSTYPE:-}" == darwin* && "$SWITCHED_TO_ZSH" -eq 0 && "$(ps -p $$ -o comm= 2>/dev/null || true)" != "zsh" ]]; then
export SWITCHED_TO_ZSH=1
exec env zsh "$0" "$@"
fi
# =========================================================
# SCRIPT PATH RESOLUTION (BASH + ZSH)
# =========================================================
if [[ -n "${BASH_SOURCE:-}" ]]; then
script_path="${BASH_SOURCE[0]}"
elif [[ -n "${ZSH_VERSION:-}" ]]; then
script_path="${(%):-%x}"
else
script_path="$0"
fi
APP_NAME="ebook2audiobook"
SCRIPT_DIR="$(cd "$(dirname "$script_path")" >/dev/null 2>&1 && pwd -P)"
SCRIPT_NAME="$(basename "$script_path")"
INSTALLED_LOG="$SCRIPT_DIR/.installed"
CONDA_HOME="$HOME/Miniforge3"
CONDA_BIN_PATH="$CONDA_HOME/bin"
# heavy directories deleted atomically (no traversal)
SKIP_DIRS=("python_env" "Miniforge3")
# macOS shortcuts (KNOWN, FIXED PATHS)
APP_BUNDLE="$HOME/Applications/$APP_NAME.app"
DESKTOP_ALIAS="$HOME/Desktop/ebook2audiobook"
# =========================================================
# HEADER
# =========================================================
echo
echo "========================================"
echo " $APP_NAME – Uninstaller"
echo "========================================"
echo "Install location:"
echo " $SCRIPT_DIR"
echo
# =========================================================
# USER CONFIRMATION
# =========================================================
if [[ -t 0 ]]; then
echo "This will uninstall $APP_NAME."
echo "Components listed in .installed will be removed."
echo
printf "Press Enter to continue or Ctrl+C to abort..."
read -r _ || true
echo
fi
# =========================================================
# macOS SHORTCUT CLEANUP (NO TESTS, GUARDED)
# =========================================================
if [[ "${OSTYPE:-}" == darwin* ]]; then
echo "Cleaning macOS shortcuts"
if [[ -n "$APP_BUNDLE" && "$APP_BUNDLE" != "/" ]]; then
rm -rf "$APP_BUNDLE" 2>/dev/null || true
fi
if [[ -n "$DESKTOP_ALIAS" && "$DESKTOP_ALIAS" != "/" ]]; then
rm -f "$DESKTOP_ALIAS" 2>/dev/null || true
fi
fi
# =========================================================
# LINUX SHORTCUT CLEANUP (NO TESTS, GUARDED)
# =========================================================
if [[ "${OSTYPE:-}" == linux* ]]; then
echo "Cleaning Linux shortcuts"
if [[ -n "$DESKTOP_ALIAS" && "$DESKTOP_ALIAS" != "/" ]]; then
rm -f "$DESKTOP_ALIAS" 2>/dev/null || true
fi
fi
# =========================================================
# PROCESS .installed (CONTROLLED REMOVAL)
# =========================================================
REMOVE_CONDA=0
if [[ -f "$INSTALLED_LOG" ]] && grep -iqF "Miniforge3" "$INSTALLED_LOG"; then
REMOVE_CONDA=1
fi
# =========================================================
# SAFE PATH CLEANUP
# =========================================================
remove_from_path() {
local target="$1"
# Refuse to operate on empty or dangerous targets
[[ -z "$target" ]] && return 0
case "$target" in
/|/bin|/usr/bin|/sbin|/usr/sbin|/usr/local/bin) return 0 ;;
esac
local new_path=""
local rest="${PATH:-}"
local p
while [[ -n "$rest" ]]; do
if [[ "$rest" == *:* ]]; then
p="${rest%%:*}"
rest="${rest#*:}"
else
p="$rest"
rest=""
fi
[[ -z "$p" ]] && continue
[[ "$p" == "$target" ]] && continue
new_path="${new_path:+$new_path:}$p"
done
PATH="$new_path"
export PATH
}
# =========================================================
# FAST DELETE HEAVY REPO DIRS (NO LISTING)
# =========================================================
for d in "${SKIP_DIRS[@]}"; do
path="$SCRIPT_DIR/$d"
if [[ -n "$path" && "$path" != "/" ]]; then
rm -rf "$path" 2>/dev/null || true
fi
done
# =========================================================
# CLEAN REPOSITORY CONTENT (FIRST LEVEL ONLY)
# =========================================================
echo
echo "Cleaning repository content..."
# Make unmatched globs expand to nothing instead of erroring (zsh) or staying literal (bash)
if [[ -n "${ZSH_VERSION:-}" ]]; then
setopt local_options null_glob
else
shopt -s nullglob 2>/dev/null || true
fi
for item in "$SCRIPT_DIR"/* "$SCRIPT_DIR"/.*; do
name="${item##*/}"
[[ "$name" == "." || "$name" == ".." ]] && continue
[[ "$name" == "$SCRIPT_NAME" ]] && continue
echo "-> $item"
if [[ -n "$item" && "$item" != "/" ]]; then
/bin/rm -rf "$item"
fi
done
# remove .installed if still present
if [[ -n "$INSTALLED_LOG" && "$INSTALLED_LOG" != "/" ]]; then
rm -f "$INSTALLED_LOG" 2>/dev/null || true
fi
# =========================================================
# FINAL MESSAGE
# =========================================================
echo
echo "================================================"
echo " Uninstallation completed."
echo
echo " The application content has been removed."
echo " Please remove the empty repository folder manually:"
echo
echo " $SCRIPT_DIR"
echo
echo "================================================"
echo
if [[ -t 0 ]]; then
printf "Press Enter to continue..."
read -r _ || true
fi
exit 0