Files
ChuBlog/scripts/uninstall.sh
T

181 lines
6.2 KiB
Bash
Raw Normal View History

#!/bin/sh
set -eu
# ChuSQL 卸载脚本(Linux/macOS):停服务、删程序与配置、
# 从 .profile 摘掉 PATH 段。数据目录默认一起删。
install_dir=''
data_dir=''
keep_data='no'
keep_config='no'
assume_yes='no'
usage() {
cat <<'EOF'
Uninstall ChuSQL (Linux/macOS).
Usage: uninstall.sh [options]
--install-dir DIR program directory (default: $HOME/.local/share/chusql)
2026-10-11 17:32:12 +08:00
--data-dir DIR data directory (default: settings.toml, then XDG data directory)
--keep-data keep the data directory, remove programs only
--keep-config keep settings.toml
--yes do not ask (required when there is no terminal)
-h, --help show this help
EOF
}
step() { echo "== $1"; }
note() { echo " $1"; }
fail() {
echo "!! $1" >&2
exit 1
}
# 停掉命令行里带这个路径的进程
stop_by_path() {
target=$1
[ -n "$target" ] || return 0
2026-10-11 17:32:12 +08:00
if [ "$(uname -s)" = Linux ]; then
for process in /proc/[0-9]*/exe; do
executable=$(readlink "$process" 2>/dev/null) || continue
[ "$executable" = "$target" ] || continue
pid=${process#/proc/}; pid=${pid%/exe}
kill "$pid" || fail "cannot stop $target ($pid)"
attempts=0
while [ -e "$process" ]; do
attempts=$((attempts + 1))
[ "$attempts" -lt 100 ] || fail "timed out stopping $target ($pid)"
sleep 0.1
done
done
return 0
fi
pids=$(ps -eo pid=,args= 2>/dev/null |
awk -v t="$target" '{ pid = $1; $1 = ""; sub(/^ +/, ""); if (index($0, t) == 1) print pid }' || true)
[ -n "$pids" ] || return 0
for pid in $pids; do
kill "$pid" 2>/dev/null || true
done
note "stopped: $pids"
sleep 1
for pid in $pids; do
kill -9 "$pid" 2>/dev/null || true
done
}
# 问一句 y/n,不是 y 就当没同意
ask_yes_no() {
printf '%s ' "$1" >&2
answer=''
IFS= read -r answer || answer=''
case "$answer" in
y | Y | yes | YES | Yes) return 0 ;;
*) return 1 ;;
esac
}
while [ $# -gt 0 ]; do
case "$1" in
-h | --help) usage; exit 0 ;;
--install-dir) install_dir="${2:-}"; shift 2 ;;
--data-dir) data_dir="${2:-}"; shift 2 ;;
--keep-data) keep_data='yes'; shift ;;
--keep-config) keep_config='yes'; shift ;;
--yes) assume_yes='yes'; shift ;;
*) fail "unknown option: $1 (try --help)" ;;
esac
done
install_dir=${install_dir:-$HOME/.local/share/chusql}
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ChuSQL"
config_file="$config_dir/settings.toml"
2026-10-11 17:32:12 +08:00
if [ -z "$data_dir" ] && [ -f "$config_file" ]; then
data_dir=$(awk '/^[[:space:]]*\[storage\]/ { storage=1; next } /^[[:space:]]*\[/ { storage=0 } storage && /^[[:space:]]*data_dir[[:space:]]*=[[:space:]]*"[^"\\]*"[[:space:]]*(#.*)?$/ { sub(/^[^=]*=[[:space:]]*"/, ""); sub(/"[[:space:]]*(#.*)?$/, ""); print; exit }' "$config_file")
[ -n "$data_dir" ] || fail 'settings.toml needs an explicit double-quoted storage.data_dir without escapes; pass --data-dir'
case "$data_dir" in /*) ;; *) data_dir="$install_dir/$data_dir" ;; esac
fi
data_dir=${data_dir:-${XDG_DATA_HOME:-$HOME/.local/share}/chusql/data}
if [ "$(uname -s)" = Linux ]; then
install_dir=$(realpath -m -- "$install_dir")
data_dir=$(realpath -m -- "$data_dir")
else
if [ -d "$install_dir" ]; then install_dir=$(CDPATH= cd -- "$install_dir" && pwd -P); fi
if [ -d "$data_dir" ]; then data_dir=$(CDPATH= cd -- "$data_dir" && pwd -P); fi
fi
case "$install_dir" in /|"$HOME") fail 'refusing to remove the root or home directory' ;; esac
case "$data_dir" in /|"$HOME") fail 'refusing to remove the root or home data directory' ;; esac
case "$install_dir" in "$data_dir"|"$data_dir"/*) fail 'data directory must not contain the installation' ;; esac
for entry in bin static csql csql-web csql-web.sh uninstall.sh; do
case "$data_dir" in "$install_dir/$entry"|"$install_dir/$entry"/*) fail 'data directory overlaps installed program files' ;; esac
done
profile="$HOME/.profile"
if [ "$assume_yes" = 'no' ]; then
echo 'ChuSQL uninstaller'
echo " install dir $install_dir"
echo " data dir $data_dir"
if [ "$keep_data" = 'yes' ]; then echo ' data kept'; fi
if [ -t 0 ]; then
ask_yes_no 'Remove it? [y/N]' || {
echo 'nothing was removed'
exit 0
}
else
fail 'no terminal: rerun with --yes to confirm'
fi
fi
step 'Stopping the service'
2026-10-05 22:22:23 +08:00
service_file="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/chusql-server.service"
if [ "$(uname -s)" = 'Linux' ] && [ -f "$service_file" ]; then
command -v systemctl >/dev/null 2>&1 || fail 'systemctl is required to remove the service'
systemctl --user disable --now chusql-server.service || fail 'cannot stop and disable chusql-server.service'
rm -f "$service_file"
systemctl --user daemon-reload || fail 'cannot reload systemd units'
fi
stop_by_path "$install_dir/bin/chusql-server"
stop_by_path "$install_dir/bin/chusql-web"
step 'Removing files'
if [ ! -d "$install_dir" ]; then
note "not found: $install_dir"
elif [ "$keep_data" = 'yes' ]; then
2026-10-11 17:32:12 +08:00
for entry in "$install_dir"/bin "$install_dir"/static "$install_dir"/csql "$install_dir"/csql-web "$install_dir"/csql-web.sh "$install_dir"/uninstall.sh; do
[ -e "$entry" ] || continue
rm -rf "$entry"
done
2026-10-11 17:32:12 +08:00
note "kept data: $data_dir"
else
rm -rf "$install_dir"
note "removed $install_dir"
fi
2026-10-11 17:32:12 +08:00
if [ "$keep_data" = 'no' ] && [ -d "$data_dir" ]; then rm -rf "$data_dir"; fi
step 'Removing settings.toml'
if [ "$keep_config" = 'yes' ]; then
note "kept config: $config_file"
else
rm -f "$config_file"
rmdir "$config_dir" 2>/dev/null || true
note "removed config: $config_file"
fi
step 'Removing the PATH entry'
marker='# ChuSQL'
if [ ! -f "$profile" ]; then
note "no $profile"
2026-10-11 17:32:12 +08:00
elif grep -q "^$marker$" "$profile"; then
tmp="$profile.chusql-uninstall"
awk -v m="$marker" '
skip > 0 { skip--; next }
2026-10-11 17:32:12 +08:00
$0 == m { skip = 2; next }
{ print }
' "$profile" >"$tmp" && mv "$tmp" "$profile"
note "removed the $marker block from $profile"
else
note "no $marker block in $profile"
fi
step 'Done'
note 'open a new terminal: the old PATH stays in the current one'