Files
clash-for-linux/uninstall.sh
T
Arvin e1c9ee49f0
Text Encoding / utf8 (push) Successful in 1m31s
fix: restore subscription update compatibility
2026-07-17 10:20:31 +08:00

214 lines
6.4 KiB
Bash
Executable File
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.
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
KEEP_RUNTIME="false"
PURGE_RUNTIME="false"
DEV_RESET="false"
REMOVE_PROJECT="false"
ASSUME_YES="false"
for arg in "$@"; do
case "$arg" in
--keep-runtime)
KEEP_RUNTIME="true"
;;
--purge-runtime)
# Compatibility alias: complete uninstall is the default now, but older
# docs/scripts may still pass --purge-runtime explicitly.
PURGE_RUNTIME="true"
KEEP_RUNTIME="false"
;;
--dev-reset)
DEV_RESET="true"
;;
--remove-project)
REMOVE_PROJECT="true"
;;
--yes|-y)
ASSUME_YES="true"
;;
*)
echo "未知参数:$arg" >&2
echo "用法:bash uninstall.sh [--keep-runtime] [--dev-reset] [--purge-runtime] [--remove-project] [--yes]" >&2
exit 2
;;
esac
done
if [ "$REMOVE_PROJECT" = "true" ] && { [ "$KEEP_RUNTIME" = "true" ] || [ "$DEV_RESET" = "true" ]; }; then
echo "--remove-project 不能与 --keep-runtime 或 --dev-reset 同时使用" >&2
exit 2
fi
source "$PROJECT_DIR/scripts/core/common.sh"
source "$PROJECT_DIR/scripts/core/runtime.sh"
source "$PROJECT_DIR/scripts/core/config.sh"
source "$PROJECT_DIR/scripts/core/proxy.sh"
source "$PROJECT_DIR/scripts/init/systemd.sh"
source "$PROJECT_DIR/scripts/init/systemd-user.sh"
source "$PROJECT_DIR/scripts/init/script.sh"
print_current_shell_proxy_cleanup_hint() {
echo
echo "如果当前终端仍然存在代理变量,请执行:"
echo "unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY no_proxy NO_PROXY"
}
print_uninstall_modes_hint() {
echo
echo "其他卸载方式:"
echo " bash uninstall.sh --keep-runtime # 只移除入口,保留 runtime 数据"
echo " bash uninstall.sh --dev-reset # 开发重置,保留订阅与下载缓存"
echo " bash uninstall.sh --remove-project # 完整卸载后移走项目目录"
}
confirm_remove_project() {
local answer
[ "$ASSUME_YES" = "true" ] && return 0
if [ ! -t 0 ]; then
echo "--remove-project 需要交互确认;非交互场景请显式传入 --yes" >&2
return 1
fi
echo
echo "即将完整卸载并移走项目目录:$PROJECT_DIR"
echo "请输入完整项目路径确认:"
IFS= read -r answer
[ "$answer" = "$PROJECT_DIR" ]
}
remove_project_dir() {
local backup_root backup_dir parent_dir
case "$PROJECT_DIR" in
""|"/"|"$HOME"|"$HOME/")
echo "拒绝移走危险路径:$PROJECT_DIR" >&2
return 1
;;
esac
[ -f "$PROJECT_DIR/install.sh" ] || {
echo "拒绝移走非项目目录:$PROJECT_DIR" >&2
return 1
}
[ -f "$PROJECT_DIR/uninstall.sh" ] || {
echo "拒绝移走非项目目录:$PROJECT_DIR" >&2
return 1
}
[ -f "$PROJECT_DIR/scripts/core/common.sh" ] || {
echo "拒绝移走非项目目录:$PROJECT_DIR" >&2
return 1
}
backup_root="${CLASH_REMOVE_PROJECT_BACKUP_ROOT:-$HOME/.local/share/clash-for-linux-backups}"
mkdir -p "$backup_root"
backup_dir="$backup_root/project.removed-$(date +%Y%m%d-%H%M%S)"
if [ -e "$backup_dir" ]; then
backup_dir="$backup_dir-$$"
fi
parent_dir="$(dirname "$PROJECT_DIR")"
cd "$parent_dir"
mv "$PROJECT_DIR" "$backup_dir"
echo "[ok] 已移走项目目录:$backup_dir"
}
init_project_context "$PROJECT_DIR"
load_env_if_exists
detect_install_scope auto
if [ "$REMOVE_PROJECT" = "true" ]; then
confirm_remove_project || {
echo "已取消移走项目目录" >&2
exit 1
}
fi
SYSTEM_PROXY_CLOSED="unknown"
if boot_proxy_keep_disable; then
SYSTEM_PROXY_CLOSED="true"
else
SYSTEM_PROXY_CLOSED="false"
fi
clear_shell_proxy_persist_state || true
stop_subconverter >/dev/null 2>&1 || true
service_stop >/dev/null 2>&1 || true
stop_runtime >/dev/null 2>&1 || true
remove_runtime_entry >/dev/null 2>&1 || true
remove_clashctl_entry >/dev/null 2>&1 || true
remove_clashctl_completion >/dev/null 2>&1 || true
remove_shell_alias_entry >/dev/null 2>&1 || true
if [ "$DEV_RESET" = "true" ] && [ "$PURGE_RUNTIME" != "true" ]; then
KEEP_RUNTIME="true"
fi
if [ "$KEEP_RUNTIME" != "true" ]; then
rm -rf "$RUNTIME_DIR"
clear_controller_secret || true
print_current_shell_proxy_cleanup_hint
echo "[ok] 已删除运行目录:$RUNTIME_DIR"
if [ "$SYSTEM_PROXY_CLOSED" = "true" ]; then
echo "[ok] 已关闭系统代理持久接管"
echo "[ok] 完整卸载:已清理服务、入口、运行目录与 controller secret"
else
echo "[error] 系统代理持久块清理失败:$(system_proxy_env_file)" >&2
echo "[info] 已清理服务、入口、运行目录与 controller secret,但系统代理仍需处理"
fi
print_uninstall_modes_hint
elif [ "$DEV_RESET" = "true" ]; then
cache_backup_dir="$(mktemp -d)"
cache_restore_needed="false"
subscriptions_backup_file="$cache_backup_dir/subscriptions.yaml"
subscriptions_restore_needed="false"
if [ -d "$RUNTIME_DIR/cache" ]; then
cp -a "$RUNTIME_DIR/cache" "$cache_backup_dir/" 2>/dev/null || true
cache_restore_needed="true"
fi
if [ -f "$RUNTIME_DIR/subscriptions.yaml" ]; then
cp -f "$RUNTIME_DIR/subscriptions.yaml" "$subscriptions_backup_file" 2>/dev/null || true
subscriptions_restore_needed="true"
fi
clean_runtime_state >/dev/null 2>&1
if [ "$cache_restore_needed" = "true" ] && [ -d "$cache_backup_dir/cache" ]; then
mkdir -p "$RUNTIME_DIR"
rm -rf "$RUNTIME_DIR/cache" 2>/dev/null || true
mv "$cache_backup_dir/cache" "$RUNTIME_DIR/cache"
fi
if [ "$subscriptions_restore_needed" = "true" ] && [ -f "$subscriptions_backup_file" ]; then
mkdir -p "$RUNTIME_DIR"
cp -f "$subscriptions_backup_file" "$RUNTIME_DIR/subscriptions.yaml"
fi
rm -rf "$cache_backup_dir" 2>/dev/null || true
clear_controller_secret || true
print_current_shell_proxy_cleanup_hint
echo "[ok] 已清理安装状态:$RUNTIME_DIR"
echo "[info] 保留内容:subscriptions.yaml、下载缓存与项目目录仍在(已清理 controller secret)"
else
print_current_shell_proxy_cleanup_hint
echo "[ok] 已卸载安装入口,保留运行目录:$RUNTIME_DIR"
echo "[info] 保留内容:runtime 数据仍在(按 --keep-runtime 请求)"
fi
if [ "$SYSTEM_PROXY_CLOSED" != "true" ]; then
echo "[error] 卸载未完成:请使用有权限的用户重新执行此脚本,或手动删除 $(system_proxy_env_file) 中的 clash-for-linux 代理块" >&2
exit 1
fi
echo "[ok] 卸载完成"
if [ "$REMOVE_PROJECT" = "true" ]; then
remove_project_dir
fi