mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-03-21 22:06:45 +08:00
322 lines
6.8 KiB
Bash
Executable File
322 lines
6.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
Server_Dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
Service_Name="clash-for-linux.service"
|
||
PROFILED_FILE="/etc/profile.d/clash-for-linux.sh"
|
||
ENV_FILE="$Server_Dir/.env"
|
||
CONF_FILE="$Server_Dir/conf/config.yaml"
|
||
TEMP_CONF_FILE="$Server_Dir/temp/config.yaml"
|
||
|
||
log() { printf "%b\n" "$*"; }
|
||
info() { log "\033[36m[INFO]\033[0m $*"; }
|
||
ok() { log "\033[32m[OK]\033[0m $*"; }
|
||
warn() { log "\033[33m[WARN]\033[0m $*"; }
|
||
err() { log "\033[31m[ERROR]\033[0m $*"; }
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
Usage:
|
||
clashctl COMMAND [OPTIONS]
|
||
|
||
Commands:
|
||
on 开启代理
|
||
off 关闭代理
|
||
status 内核状况
|
||
proxy 系统代理
|
||
ui Web 面板
|
||
secret Web 密钥
|
||
sub 订阅管理
|
||
upgrade 升级内核
|
||
tun Tun 模式
|
||
mixin Mixin 配置
|
||
|
||
Global Options:
|
||
-h, --help 显示帮助信息
|
||
|
||
Examples:
|
||
clashctl on
|
||
clashctl off
|
||
clashctl status
|
||
clashctl proxy status
|
||
clashctl ui
|
||
clashctl secret
|
||
clashctl sub show
|
||
clashctl sub update
|
||
clashctl tun status
|
||
clashctl mixin status
|
||
|
||
💡 clashon 同 clashctl on,Tab 补全更方便!
|
||
EOF
|
||
}
|
||
|
||
require_profiled() {
|
||
if [ ! -f "$PROFILED_FILE" ]; then
|
||
err "未安装 Shell 代理快捷命令:$PROFILED_FILE"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
has_systemd() {
|
||
command -v systemctl >/dev/null 2>&1
|
||
}
|
||
|
||
service_exists() {
|
||
has_systemd || return 1
|
||
|
||
local load_state
|
||
load_state="$(systemctl show "$Service_Name" --property=LoadState --value 2>/dev/null || true)"
|
||
|
||
[ -n "$load_state" ] && [ "$load_state" != "not-found" ]
|
||
}
|
||
|
||
service_is_active() {
|
||
service_exists || return 1
|
||
systemctl is-active --quiet "$Service_Name"
|
||
}
|
||
|
||
read_config_value() {
|
||
local key="$1"
|
||
local f
|
||
for f in "$TEMP_CONF_FILE" "$CONF_FILE"; do
|
||
if [ -f "$f" ]; then
|
||
sed -nE "s/^[[:space:]]*${key}:[[:space:]]*//p" "$f" \
|
||
| head -n 1 \
|
||
| tr -d '\r' \
|
||
| sed -E 's/^"(.*)"$/\1/; s/^'\''(.*)'\''$/\1/'
|
||
return 0
|
||
fi
|
||
done
|
||
return 1
|
||
}
|
||
|
||
cmd_on() {
|
||
require_profiled
|
||
# shellcheck disable=SC1090
|
||
source "$PROFILED_FILE"
|
||
proxy_on
|
||
}
|
||
|
||
cmd_off() {
|
||
require_profiled
|
||
# shellcheck disable=SC1090
|
||
source "$PROFILED_FILE"
|
||
proxy_off
|
||
}
|
||
|
||
cmd_status() {
|
||
local proc_info=""
|
||
proc_info="$(ps -eo pid,ppid,cmd | grep -E '[c]lash-linux|[m]ihomo' || true)"
|
||
|
||
echo "=== Clash Status ==="
|
||
|
||
if service_exists; then
|
||
local active_state enabled_state
|
||
active_state="$(systemctl is-active "$Service_Name" 2>/dev/null || true)"
|
||
enabled_state="$(systemctl is-enabled "$Service_Name" 2>/dev/null || true)"
|
||
|
||
echo "Service: installed"
|
||
echo "Active : ${active_state:-unknown}"
|
||
echo "Enabled: ${enabled_state:-unknown}"
|
||
|
||
if [ -n "$proc_info" ]; then
|
||
echo
|
||
echo "Process:"
|
||
echo "$proc_info"
|
||
fi
|
||
|
||
return 0
|
||
fi
|
||
|
||
if [ -n "$proc_info" ]; then
|
||
warn "未检测到 systemd 服务,但发现 Clash 进程正在运行"
|
||
echo
|
||
echo "Process:"
|
||
echo "$proc_info"
|
||
return 0
|
||
fi
|
||
|
||
warn "未检测到 systemd 服务,也未发现 Clash 进程"
|
||
return 1
|
||
}
|
||
|
||
cmd_proxy() {
|
||
require_profiled
|
||
# shellcheck disable=SC1090
|
||
source "$PROFILED_FILE"
|
||
|
||
local sub="${1:-status}"
|
||
case "$sub" in
|
||
on) proxy_on ;;
|
||
off) proxy_off ;;
|
||
status) proxy_status ;;
|
||
*)
|
||
err "未知 proxy 子命令: $sub"
|
||
echo "用法: clashctl proxy [on|off|status]"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
cmd_ui() {
|
||
local controller host port
|
||
controller="$(read_config_value "external-controller" || true)"
|
||
[ -n "${controller:-}" ] || controller="127.0.0.1:9090"
|
||
|
||
host="${controller%:*}"
|
||
port="${controller##*:}"
|
||
|
||
case "$host" in
|
||
0.0.0.0|::|localhost)
|
||
host="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||
[ -n "$host" ] || host="127.0.0.1"
|
||
;;
|
||
esac
|
||
|
||
printf 'http://%s:%s/ui\n' "$host" "$port"
|
||
}
|
||
|
||
cmd_secret() {
|
||
local secret
|
||
secret="$(read_config_value "secret" || true)"
|
||
if [ -n "${secret:-}" ]; then
|
||
echo "$secret"
|
||
else
|
||
err "未读取到 secret"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
cmd_sub() {
|
||
local subcmd="${1:-show}"
|
||
case "$subcmd" in
|
||
show)
|
||
if [ -f "$ENV_FILE" ]; then
|
||
grep -E '^[[:space:]]*(export[[:space:]]+)?CLASH_URL=' "$ENV_FILE" || true
|
||
else
|
||
err "未找到 .env"
|
||
exit 1
|
||
fi
|
||
;;
|
||
update)
|
||
if service_exists; then
|
||
info "重启服务以刷新订阅..."
|
||
systemctl restart "$Service_Name"
|
||
ok "订阅刷新完成"
|
||
elif [ -f "$Server_Dir/restart.sh" ]; then
|
||
bash "$Server_Dir/restart.sh"
|
||
ok "订阅刷新完成"
|
||
else
|
||
err "未找到 restart.sh"
|
||
exit 1
|
||
fi
|
||
;;
|
||
*)
|
||
err "未知 sub 子命令: $subcmd"
|
||
echo "用法: clashctl sub [show|update]"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
cmd_upgrade() {
|
||
if [ -f "$Server_Dir/update.sh" ]; then
|
||
bash "$Server_Dir/update.sh"
|
||
else
|
||
err "未找到 update.sh"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
write_env_bool() {
|
||
local key="$1"
|
||
local value="$2"
|
||
|
||
if [ ! -f "$ENV_FILE" ]; then
|
||
err "未找到 .env"
|
||
exit 1
|
||
fi
|
||
|
||
if grep -qE "^[[:space:]]*(export[[:space:]]+)?${key}=" "$ENV_FILE"; then
|
||
sed -i -E "s|^[[:space:]]*(export[[:space:]]+)?${key}=.*$|export ${key}=\"${value}\"|g" "$ENV_FILE"
|
||
else
|
||
echo "export ${key}=\"${value}\"" >> "$ENV_FILE"
|
||
fi
|
||
}
|
||
|
||
cmd_tun() {
|
||
local subcmd="${1:-status}"
|
||
case "$subcmd" in
|
||
status)
|
||
if [ -f "$ENV_FILE" ]; then
|
||
grep -E '^[[:space:]]*(export[[:space:]]+)?CLASH_TUN=' "$ENV_FILE" || echo 'CLASH_TUN=未配置'
|
||
else
|
||
err "未找到 .env"
|
||
exit 1
|
||
fi
|
||
;;
|
||
on)
|
||
write_env_bool "CLASH_TUN" "true"
|
||
ok "已写入 CLASH_TUN=true"
|
||
;;
|
||
off)
|
||
write_env_bool "CLASH_TUN" "false"
|
||
ok "已写入 CLASH_TUN=false"
|
||
;;
|
||
*)
|
||
err "未知 tun 子命令: $subcmd"
|
||
echo "用法: clashctl tun [on|off|status]"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
cmd_mixin() {
|
||
local subcmd="${1:-status}"
|
||
local mixin_file="$Server_Dir/conf/mixin.yaml"
|
||
|
||
case "$subcmd" in
|
||
status)
|
||
if [ -f "$mixin_file" ]; then
|
||
ok "Mixin 已存在: $mixin_file"
|
||
else
|
||
warn "Mixin 不存在: $mixin_file"
|
||
fi
|
||
;;
|
||
edit)
|
||
${EDITOR:-vi} "$mixin_file"
|
||
;;
|
||
*)
|
||
err "未知 mixin 子命令: $subcmd"
|
||
echo "用法: clashctl mixin [status|edit]"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
main() {
|
||
local cmd="${1:-}"
|
||
shift || true
|
||
|
||
case "$cmd" in
|
||
on) cmd_on "$@" ;;
|
||
off) cmd_off "$@" ;;
|
||
status) cmd_status "$@" ;;
|
||
proxy) cmd_proxy "$@" ;;
|
||
ui) cmd_ui "$@" ;;
|
||
secret) cmd_secret "$@" ;;
|
||
sub) cmd_sub "$@" ;;
|
||
upgrade) cmd_upgrade "$@" ;;
|
||
tun) cmd_tun "$@" ;;
|
||
mixin) cmd_mixin "$@" ;;
|
||
-h|--help|"") usage ;;
|
||
*)
|
||
err "未知命令: $cmd"
|
||
echo
|
||
usage
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
main "$@" |