2026-10-03 02:17:46 +08:00
#!/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)
2026-10-03 02:17:46 +08:00
--keep-data keep the data directory, remove programs only
2026-10-05 00:15:01 +08:00
--keep-config keep settings.toml
2026-10-03 02:17:46 +08:00
--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
2026-10-03 02:17:46 +08:00
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"
2026-10-05 00:15:01 +08:00
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
2026-10-03 02:17:46 +08:00
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
2026-10-03 02:17:46 +08:00
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
2026-10-03 02:17:46 +08:00
[ -e " $entry " ] || continue
rm -rf " $entry "
done
2026-10-11 17:32:12 +08:00
note "kept data: $data_dir "
2026-10-03 02:17:46 +08:00
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
2026-10-03 02:17:46 +08:00
2026-10-05 00:15:01 +08:00
step 'Removing settings.toml'
2026-10-03 02:17:46 +08:00
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
2026-10-03 02:17:46 +08:00
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 }
2026-10-03 02:17:46 +08:00
{ 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'