mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-10-10 04:03:04 +08:00
This commit is contained in:
@@ -143,6 +143,8 @@ OpenWrt 下 root/system 安装会把 `clashctl`、`clashon`、`clashoff` 等命
|
||||
clashctl add <订阅链接> <名称>
|
||||
clashctl use
|
||||
clashctl ls
|
||||
clashctl sub update
|
||||
clashsub update # 兼容旧版命令
|
||||
clashctl sub
|
||||
clashctl sub list
|
||||
clashctl sub enable <名称>
|
||||
|
||||
@@ -396,6 +396,10 @@ clashls() {
|
||||
clashctl ls "$@"
|
||||
}
|
||||
|
||||
clashsub() {
|
||||
clashctl sub "$@"
|
||||
}
|
||||
|
||||
clashselect() {
|
||||
clashctl select "$@"
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ Usage:
|
||||
|
||||
📦 Subscription:
|
||||
config show 📡 查看当前订阅
|
||||
config regen 🔄 更新当前订阅
|
||||
sub update 🔄 更新当前订阅(兼容:clashsub update)
|
||||
ls 📜 查看订阅列表
|
||||
sub 📡 订阅高级管理(启用 / 禁用 / 重命名 / 删除)
|
||||
sub 📡 订阅管理(更新 / 启用 / 禁用 / 重命名 / 删除)
|
||||
|
||||
🕹️ Control:
|
||||
clashui 🕹️ 查看 Web 控制台
|
||||
@@ -69,6 +69,7 @@ Usage:
|
||||
completion 💡 导出 Bash / Zsh 补全脚本
|
||||
|
||||
📌 Advanced Examples:
|
||||
clashctl sub update
|
||||
clashctl sub list
|
||||
clashctl sub enable hk
|
||||
clashctl sub disable hk
|
||||
@@ -7047,6 +7048,12 @@ cmd_sub() {
|
||||
prepare
|
||||
|
||||
case "${1:-}" in
|
||||
update)
|
||||
regenerate_config
|
||||
apply_runtime_change_after_config_mutation
|
||||
print_config_regen_feedback
|
||||
print_config_apply_feedback
|
||||
;;
|
||||
list)
|
||||
shift || true
|
||||
cmd_ls "$@"
|
||||
@@ -7101,8 +7108,9 @@ cmd_sub() {
|
||||
cmd_health "$@"
|
||||
;;
|
||||
"")
|
||||
ui_title "📡 订阅高级管理"
|
||||
ui_title "📡 订阅管理"
|
||||
echo "📜 用法:"
|
||||
echo " clashctl sub update"
|
||||
echo " clashctl sub list"
|
||||
echo " clashctl sub use <名称>"
|
||||
echo " clashctl sub enable <名称>"
|
||||
@@ -7112,11 +7120,12 @@ cmd_sub() {
|
||||
echo " clashctl sub health [名称]"
|
||||
echo
|
||||
echo "🧩 说明:"
|
||||
echo " add / use / ls 属于主路径"
|
||||
echo " add / use / ls / sub update 属于主路径"
|
||||
echo " health 保留为多订阅健康审计"
|
||||
echo " sub 仅用于高级维护操作"
|
||||
echo " 其他 sub 子命令用于高级维护操作"
|
||||
echo
|
||||
echo "💡 常用动作:"
|
||||
echo " clashctl sub update"
|
||||
echo " clashctl sub list"
|
||||
echo " clashctl sub health"
|
||||
echo " clashctl sub enable <名称>"
|
||||
|
||||
@@ -2599,6 +2599,7 @@ install_alias_command_wrappers() {
|
||||
clashon \
|
||||
clashoff \
|
||||
clashproxy \
|
||||
clashsub \
|
||||
clashls \
|
||||
clashselect \
|
||||
clashui \
|
||||
@@ -2897,6 +2898,7 @@ remove_alias_command_wrappers() {
|
||||
clashon \
|
||||
clashoff \
|
||||
clashproxy \
|
||||
clashsub \
|
||||
clashls \
|
||||
clashselect \
|
||||
clashui \
|
||||
|
||||
@@ -224,7 +224,7 @@ _clash_for_linux_complete_sub() {
|
||||
COMPREPLY=()
|
||||
|
||||
if [ "$rel_index" -eq 1 ]; then
|
||||
_clash_for_linux_add_matches "$cur" list use set enable disable rename remove rm del health help -h --help
|
||||
_clash_for_linux_add_matches "$cur" update list use set enable disable rename remove rm del health help -h --help
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -339,6 +339,13 @@ _clash_for_linux_complete_command() {
|
||||
arg2="${COMP_WORDS[3]:-}"
|
||||
arg3="${COMP_WORDS[4]:-}"
|
||||
;;
|
||||
clashsub)
|
||||
canonical="sub"
|
||||
rel_index=$COMP_CWORD
|
||||
arg1="${COMP_WORDS[1]:-}"
|
||||
arg2="${COMP_WORDS[2]:-}"
|
||||
arg3="${COMP_WORDS[3]:-}"
|
||||
;;
|
||||
clashrelay)
|
||||
canonical="relay"
|
||||
rel_index=$COMP_CWORD
|
||||
@@ -396,6 +403,7 @@ _clash_for_linux_complete_command() {
|
||||
}
|
||||
|
||||
complete -F _clash_for_linux_complete_command clashctl
|
||||
complete -F _clash_for_linux_complete_command clashsub
|
||||
complete -F _clash_for_linux_complete_command clashrelay
|
||||
complete -F _clash_for_linux_complete_command clashmixin
|
||||
complete -F _clash_for_linux_complete_command clashsecret
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
source_clashctl_for_tests() {
|
||||
set -- ""
|
||||
# Source the real command functions; suppress the no-arg usage printed by the dispatcher.
|
||||
source "$PROJECT_DIR/scripts/core/clashctl.sh" >/dev/null
|
||||
}
|
||||
|
||||
source_clashctl_for_tests
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
|
||||
calls_file="$tmp_dir/sub-update-calls"
|
||||
output_file="$tmp_dir/sub-update-output"
|
||||
|
||||
prepare() { :; }
|
||||
regenerate_config() { printf 'regenerate\n' >> "$calls_file"; }
|
||||
apply_runtime_change_after_config_mutation() { printf 'apply\n' >> "$calls_file"; }
|
||||
print_config_regen_feedback() { printf 'regen-feedback\n' >> "$calls_file"; }
|
||||
print_config_apply_feedback() { printf 'apply-feedback\n' >> "$calls_file"; }
|
||||
|
||||
: > "$calls_file"
|
||||
if ! ( cmd_sub update ) > "$output_file" 2>&1; then
|
||||
echo "not ok - clashctl sub update should succeed" >&2
|
||||
sed 's/^/ /' "$output_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expected_calls="$(printf '%s\n' regenerate apply regen-feedback apply-feedback)"
|
||||
actual_calls="$(cat "$calls_file")"
|
||||
if [ "$actual_calls" != "$expected_calls" ]; then
|
||||
echo "not ok - clashctl sub update should follow the config regen path" >&2
|
||||
printf 'expected:\n%s\nactual:\n%s\n' "$expected_calls" "$actual_calls" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ok - clashctl sub update follows the config regen path"
|
||||
|
||||
# Verify both the sourced-shell compatibility function and the installed command wrapper.
|
||||
# shellcheck source=../core/common.sh
|
||||
source "$PROJECT_DIR/scripts/core/common.sh"
|
||||
|
||||
fixture_bin="$tmp_dir/bin"
|
||||
mkdir -p "$fixture_bin"
|
||||
|
||||
PROJECT_DIR="$PROJECT_DIR"
|
||||
alias_source_file() { printf '%s\n' "$PROJECT_DIR/scripts/core/alias.sh"; }
|
||||
command_install_dir() { printf '%s\n' "$fixture_bin"; }
|
||||
|
||||
install_alias_command_wrappers
|
||||
|
||||
if [ ! -x "$fixture_bin/clashsub" ]; then
|
||||
echo "not ok - install should create the clashsub compatibility wrapper" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat > "$fixture_bin/clashctl-bin" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s\n' "$*"
|
||||
EOF
|
||||
chmod +x "$fixture_bin/clashctl-bin"
|
||||
|
||||
wrapper_output="$(
|
||||
env -i \
|
||||
HOME="$tmp_dir/home" \
|
||||
PATH="$fixture_bin:/usr/bin:/bin" \
|
||||
CLASH_SHELL_AUTO_RESTORE_PROXY=false \
|
||||
"$fixture_bin/clashsub" update
|
||||
)"
|
||||
|
||||
if [ "$wrapper_output" != "sub update" ]; then
|
||||
echo "not ok - clashsub update should delegate to clashctl sub update" >&2
|
||||
printf 'actual: %s\n' "$wrapper_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remove_alias_command_wrappers
|
||||
if [ -e "$fixture_bin/clashsub" ]; then
|
||||
echo "not ok - uninstall should remove the clashsub compatibility wrapper" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ok - clashsub update compatibility wrapper is installed and removed"
|
||||
|
||||
completion_file="$tmp_dir/clashctl-completion.bash"
|
||||
bash "$PROJECT_DIR/scripts/core/clashctl.sh" completion bash > "$completion_file"
|
||||
|
||||
completion_output="$(
|
||||
bash --noprofile --norc -c '
|
||||
source "$1"
|
||||
COMP_WORDS=(clashsub u)
|
||||
COMP_CWORD=1
|
||||
_clash_for_linux_complete_command
|
||||
printf "%s\n" "${COMPREPLY[@]}"
|
||||
' bash "$completion_file"
|
||||
)"
|
||||
|
||||
if ! printf '%s\n' "$completion_output" | grep -Fxq update; then
|
||||
echo "not ok - clashsub should complete the update subcommand" >&2
|
||||
printf 'actual: %s\n' "$completion_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ok - clashsub update is available in shell completion"
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
|
||||
test_project="$tmp_dir/project"
|
||||
test_home="$tmp_dir/home"
|
||||
output_file="$tmp_dir/uninstall-output"
|
||||
|
||||
mkdir -p "$test_project" "$test_home" "$test_project/runtime"
|
||||
cp "$PROJECT_DIR/uninstall.sh" "$test_project/uninstall.sh"
|
||||
cp -R "$PROJECT_DIR/scripts" "$test_project/scripts"
|
||||
cp -R "$PROJECT_DIR/config" "$test_project/config"
|
||||
cp "$PROJECT_DIR/.env" "$test_project/.env"
|
||||
|
||||
# Override external effects in the isolated fixture while preserving the real
|
||||
# uninstall control flow and forcing the exact /etc/environment cleanup failure.
|
||||
cat >> "$test_project/scripts/init/script.sh" <<'EOF'
|
||||
boot_proxy_keep_disable() { return 2; }
|
||||
system_proxy_env_file() { printf '%s\n' /etc/environment; }
|
||||
clear_shell_proxy_persist_state() { :; }
|
||||
stop_subconverter() { :; }
|
||||
service_stop() { :; }
|
||||
stop_runtime() { :; }
|
||||
remove_runtime_entry() { :; }
|
||||
remove_clashctl_entry() { :; }
|
||||
remove_clashctl_completion() { :; }
|
||||
remove_shell_alias_entry() { :; }
|
||||
clear_controller_secret() { :; }
|
||||
EOF
|
||||
|
||||
if env -i \
|
||||
HOME="$test_home" \
|
||||
PATH="/usr/bin:/bin" \
|
||||
bash "$test_project/uninstall.sh" > "$output_file" 2>&1; then
|
||||
echo "not ok - uninstall should fail when /etc/environment cleanup fails" >&2
|
||||
sed 's/^/ /' "$output_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -Fq '[error]' "$output_file" || ! grep -Fq '/etc/environment' "$output_file"; then
|
||||
echo "not ok - uninstall should clearly report the system proxy cleanup error" >&2
|
||||
sed 's/^/ /' "$output_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -Fq '[ok] 卸载完成' "$output_file"; then
|
||||
echo "not ok - uninstall must not claim success after system proxy cleanup fails" >&2
|
||||
sed 's/^/ /' "$output_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ok - uninstall fails clearly when /etc/environment cleanup fails"
|
||||
+8
-2
@@ -154,10 +154,11 @@ if [ "$KEEP_RUNTIME" != "true" ]; then
|
||||
echo "[ok] 已删除运行目录:$RUNTIME_DIR"
|
||||
if [ "$SYSTEM_PROXY_CLOSED" = "true" ]; then
|
||||
echo "[ok] 已关闭系统代理持久接管"
|
||||
echo "[ok] 完整卸载:已清理服务、入口、运行目录与 controller secret"
|
||||
else
|
||||
echo "[warn] 未能确认系统代理持久块已关闭,请检查:$(system_proxy_env_file)"
|
||||
echo "[error] 系统代理持久块清理失败:$(system_proxy_env_file)" >&2
|
||||
echo "[info] 已清理服务、入口、运行目录与 controller secret,但系统代理仍需处理"
|
||||
fi
|
||||
echo "[ok] 完整卸载:已清理服务、入口、运行目录与 controller secret"
|
||||
print_uninstall_modes_hint
|
||||
elif [ "$DEV_RESET" = "true" ]; then
|
||||
cache_backup_dir="$(mktemp -d)"
|
||||
@@ -200,6 +201,11 @@ else
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user