mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-02-04 10:11:28 +08:00
Merge pull request #101 from wnlen/codex/add-one-click-installation-script
Add one‑click install/uninstall and auto-download Clash core by architecture
This commit is contained in:
25
README.md
25
README.md
@ -88,6 +88,31 @@ $ source /etc/profile.d/clash-for-linux.sh
|
|||||||
$ proxy_on
|
$ proxy_on
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## 一键安装/卸载
|
||||||
|
|
||||||
|
脚本会自动识别安装路径、创建低权限用户、检测端口冲突,并根据架构自动下载 Clash 内核(可通过 `CLASH_DOWNLOAD_URL_TEMPLATE` 自定义下载地址)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ sudo bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
如需调整安装路径或服务行为,可使用以下环境变量:
|
||||||
|
|
||||||
|
- `CLASH_INSTALL_DIR`:默认 `/opt/clash-for-linux`
|
||||||
|
- `CLASH_SERVICE_USER` / `CLASH_SERVICE_GROUP`:systemd 运行用户/组
|
||||||
|
- `CLASH_ENABLE_SERVICE`:是否 `systemctl enable`(默认 `true`)
|
||||||
|
- `CLASH_START_SERVICE`:是否 `systemctl start`(默认 `true`)
|
||||||
|
- `CLASH_AUTO_DOWNLOAD`:是否自动下载 Clash 内核(默认 `auto`)
|
||||||
|
- `CLASH_DOWNLOAD_URL_TEMPLATE`:自定义下载模板(默认 `https://github.com/Dreamacro/clash/releases/latest/download/clash-{arch}.gz`)
|
||||||
|
|
||||||
|
卸载:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ sudo bash uninstall.sh
|
||||||
|
```
|
||||||
|
|
||||||
- 检查服务端口
|
- 检查服务端口
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
112
install.sh
Executable file
112
install.sh
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
Server_Dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
Install_Dir="${CLASH_INSTALL_DIR:-/opt/clash-for-linux}"
|
||||||
|
Service_Name="clash-for-linux"
|
||||||
|
Service_User="${CLASH_SERVICE_USER:-clash}"
|
||||||
|
Service_Group="${CLASH_SERVICE_GROUP:-$Service_User}"
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo -e "\033[31m[ERROR] 需要 root 权限执行安装脚本\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${Server_Dir}/.env" ]; then
|
||||||
|
echo -e "\033[31m[ERROR] 未找到 .env 文件,请确认脚本所在目录\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$Install_Dir"
|
||||||
|
if [ "$Server_Dir" != "$Install_Dir" ]; then
|
||||||
|
if command -v rsync >/dev/null 2>&1; then
|
||||||
|
rsync -a --delete --exclude '.git' "$Server_Dir/" "$Install_Dir/"
|
||||||
|
else
|
||||||
|
cp -a "$Server_Dir/." "$Install_Dir/"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$Install_Dir"/*.sh 2>/dev/null || true
|
||||||
|
chmod +x "$Install_Dir"/scripts/* 2>/dev/null || true
|
||||||
|
chmod +x "$Install_Dir"/bin/* 2>/dev/null || true
|
||||||
|
|
||||||
|
source "$Install_Dir/.env"
|
||||||
|
source "$Install_Dir/scripts/get_cpu_arch.sh"
|
||||||
|
source "$Install_Dir/scripts/resolve_clash.sh"
|
||||||
|
|
||||||
|
if [[ -z "${CpuArch:-}" ]]; then
|
||||||
|
echo -e "\033[31m[ERROR] 无法识别 CPU 架构\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASH_HTTP_PORT=${CLASH_HTTP_PORT:-7890}
|
||||||
|
CLASH_SOCKS_PORT=${CLASH_SOCKS_PORT:-7891}
|
||||||
|
CLASH_REDIR_PORT=${CLASH_REDIR_PORT:-7892}
|
||||||
|
EXTERNAL_CONTROLLER=${EXTERNAL_CONTROLLER:-127.0.0.1:9090}
|
||||||
|
|
||||||
|
parse_port() {
|
||||||
|
local raw="$1"
|
||||||
|
raw="${raw##*:}"
|
||||||
|
echo "$raw"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_port_in_use() {
|
||||||
|
local port="$1"
|
||||||
|
if command -v ss >/dev/null 2>&1; then
|
||||||
|
ss -lnt | awk '{print $4}' | grep -E "(:|\.)${port}$" >/dev/null 2>&1
|
||||||
|
elif command -v netstat >/dev/null 2>&1; then
|
||||||
|
netstat -lnt | awk '{print $4}' | grep -E "(:|\.)${port}$" >/dev/null 2>&1
|
||||||
|
elif command -v lsof >/dev/null 2>&1; then
|
||||||
|
lsof -iTCP -sTCP:LISTEN -P -n | awk '{print $9}' | grep -E "(:|\.)${port}$" >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
echo -e "\033[33m[WARN] 未找到端口检测工具,跳过端口冲突检测\033[0m"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
Port_Conflicts=()
|
||||||
|
for port in "$CLASH_HTTP_PORT" "$CLASH_SOCKS_PORT" "$CLASH_REDIR_PORT" "$(parse_port "$EXTERNAL_CONTROLLER")"; do
|
||||||
|
if [[ "$port" =~ ^[0-9]+$ ]]; then
|
||||||
|
if is_port_in_use "$port"; then
|
||||||
|
Port_Conflicts+=("$port")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#Port_Conflicts[@]}" -ne 0 ]; then
|
||||||
|
echo -e "\033[31m[ERROR] 检测到端口冲突: ${Port_Conflicts[*]}\033[0m"
|
||||||
|
echo -e "请修改 .env 中的端口设置后重新安装。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! getent group "$Service_Group" >/dev/null 2>&1; then
|
||||||
|
groupadd --system "$Service_Group"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! id "$Service_User" >/dev/null 2>&1; then
|
||||||
|
useradd --system --no-create-home --shell /usr/sbin/nologin --gid "$Service_Group" "$Service_User"
|
||||||
|
fi
|
||||||
|
|
||||||
|
install -d -m 0755 "$Install_Dir/conf" "$Install_Dir/logs" "$Install_Dir/temp"
|
||||||
|
chown -R "$Service_User:$Service_Group" "$Install_Dir/conf" "$Install_Dir/logs" "$Install_Dir/temp"
|
||||||
|
|
||||||
|
if ! resolve_clash_bin "$Install_Dir" "$CpuArch" >/dev/null 2>&1; then
|
||||||
|
echo -e "\033[31m[ERROR] Clash 内核未就绪,请检查下载配置或手动放置二进制\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
CLASH_SERVICE_USER="$Service_User" CLASH_SERVICE_GROUP="$Service_Group" "$Install_Dir/scripts/install_systemd.sh"
|
||||||
|
if [ "${CLASH_ENABLE_SERVICE:-true}" = "true" ]; then
|
||||||
|
systemctl enable "${Service_Name}.service" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
if [ "${CLASH_START_SERVICE:-true}" = "true" ]; then
|
||||||
|
systemctl start "${Service_Name}.service" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\033[33m[WARN] 未检测到 systemd,已跳过服务单元生成\033[0m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\033[32m[OK] Clash for Linux 已安装至: ${Install_Dir}\033[0m"
|
||||||
|
echo -e "请编辑 ${Install_Dir}/.env 配置订阅地址后启动服务。"
|
||||||
118
scripts/resolve_clash.sh
Executable file
118
scripts/resolve_clash.sh
Executable file
@ -0,0 +1,118 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
resolve_clash_arch() {
|
||||||
|
local raw_arch="$1"
|
||||||
|
case "$raw_arch" in
|
||||||
|
x86_64|amd64)
|
||||||
|
echo "linux-amd64"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
echo "linux-arm64"
|
||||||
|
;;
|
||||||
|
armv7*|armv7l)
|
||||||
|
echo "linux-armv7"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "linux-${raw_arch}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
download_clash_bin() {
|
||||||
|
local server_dir="$1"
|
||||||
|
local detected_arch="$2"
|
||||||
|
local resolved_arch
|
||||||
|
local download_url
|
||||||
|
local download_target
|
||||||
|
local archive_file
|
||||||
|
|
||||||
|
resolved_arch=$(resolve_clash_arch "$detected_arch")
|
||||||
|
if [ -z "$resolved_arch" ]; then
|
||||||
|
echo -e "\033[33m[WARN] 无法识别 CPU 架构,跳过 Clash 内核自动下载\033[0m"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${CLASH_AUTO_DOWNLOAD:-auto}" = "false" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_url="${CLASH_DOWNLOAD_URL_TEMPLATE:-https://github.com/Dreamacro/clash/releases/latest/download/clash-{arch}.gz}"
|
||||||
|
if [ -z "$download_url" ]; then
|
||||||
|
echo -e "\033[33m[WARN] 未设置 CLASH_DOWNLOAD_URL_TEMPLATE,跳过 Clash 内核自动下载\033[0m"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_url="${download_url//\{arch\}/${resolved_arch}}"
|
||||||
|
download_target="${server_dir}/bin/clash-${resolved_arch}"
|
||||||
|
archive_file="${server_dir}/temp/clash-${resolved_arch}.download"
|
||||||
|
|
||||||
|
mkdir -p "${server_dir}/bin" "${server_dir}/temp"
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl -L -sS -o "${archive_file}" "${download_url}"
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
wget -q -O "${archive_file}" "${download_url}"
|
||||||
|
else
|
||||||
|
echo -e "\033[33m[WARN] 未找到 curl 或 wget,无法自动下载 Clash 内核\033[0m"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${archive_file}" ]; then
|
||||||
|
if gzip -t "${archive_file}" >/dev/null 2>&1; then
|
||||||
|
gzip -dc "${archive_file}" >"${download_target}"
|
||||||
|
else
|
||||||
|
mv "${archive_file}" "${download_target}"
|
||||||
|
fi
|
||||||
|
chmod +x "${download_target}"
|
||||||
|
echo "${download_target}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\033[33m[WARN] Clash 内核自动下载失败\033[0m"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_clash_bin() {
|
||||||
|
local server_dir="$1"
|
||||||
|
local detected_arch="$2"
|
||||||
|
local resolved_arch
|
||||||
|
local candidates=()
|
||||||
|
local candidate
|
||||||
|
local downloaded_bin
|
||||||
|
|
||||||
|
if [ -n "${CLASH_BIN:-}" ]; then
|
||||||
|
if [ -x "$CLASH_BIN" ]; then
|
||||||
|
echo "$CLASH_BIN"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo -e "\033[31m[ERROR] CLASH_BIN 指定的文件不可执行: $CLASH_BIN\033[0m"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
resolved_arch=$(resolve_clash_arch "$detected_arch")
|
||||||
|
if [ -n "$resolved_arch" ]; then
|
||||||
|
candidates+=("${server_dir}/bin/clash-${resolved_arch}")
|
||||||
|
fi
|
||||||
|
candidates+=(
|
||||||
|
"${server_dir}/bin/clash-${detected_arch}"
|
||||||
|
"${server_dir}/bin/clash"
|
||||||
|
)
|
||||||
|
|
||||||
|
for candidate in "${candidates[@]}"; do
|
||||||
|
if [ -x "$candidate" ]; then
|
||||||
|
echo "$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if downloaded_bin=$(download_clash_bin "$server_dir" "$detected_arch"); then
|
||||||
|
echo "$downloaded_bin"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\033[31m\n[ERROR] 未找到可用的 Clash 二进制。\033[0m"
|
||||||
|
echo -e "请将对应架构的二进制放入: $server_dir/bin/"
|
||||||
|
echo -e "可用命名示例: clash-${resolved_arch} 或 clash-${detected_arch}"
|
||||||
|
echo -e "或通过 CLASH_BIN 指定自定义路径。"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
58
start.sh
58
start.sh
@ -97,6 +97,8 @@ if [[ -z "$CpuArch" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
source "$Server_Dir/scripts/resolve_clash.sh"
|
||||||
|
|
||||||
|
|
||||||
## 临时取消环境变量
|
## 临时取消环境变量
|
||||||
unset http_proxy
|
unset http_proxy
|
||||||
@ -223,66 +225,12 @@ fi
|
|||||||
sed -r -i '/^secret: /s@(secret: ).*@\1'${Secret}'@g' $Conf_Dir/config.yaml
|
sed -r -i '/^secret: /s@(secret: ).*@\1'${Secret}'@g' $Conf_Dir/config.yaml
|
||||||
|
|
||||||
|
|
||||||
resolve_clash_arch() {
|
|
||||||
local raw_arch="$1"
|
|
||||||
case "$raw_arch" in
|
|
||||||
x86_64|amd64)
|
|
||||||
echo "linux-amd64"
|
|
||||||
;;
|
|
||||||
aarch64|arm64)
|
|
||||||
echo "linux-arm64"
|
|
||||||
;;
|
|
||||||
armv7*|armv7l)
|
|
||||||
echo "linux-armv7"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "linux-${raw_arch}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_clash_bin() {
|
|
||||||
local detected_arch="${CpuArch:-$(uname -m 2>/dev/null)}"
|
|
||||||
local resolved_arch
|
|
||||||
local candidates=()
|
|
||||||
|
|
||||||
if [ -n "$CLASH_BIN" ]; then
|
|
||||||
if [ -x "$CLASH_BIN" ]; then
|
|
||||||
echo "$CLASH_BIN"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
echo -e "\033[31m[ERROR] CLASH_BIN 指定的文件不可执行: $CLASH_BIN\033[0m"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
resolved_arch=$(resolve_clash_arch "$detected_arch")
|
|
||||||
if [ -n "$resolved_arch" ]; then
|
|
||||||
candidates+=("$Server_Dir/bin/clash-${resolved_arch}")
|
|
||||||
fi
|
|
||||||
candidates+=(
|
|
||||||
"$Server_Dir/bin/clash-${detected_arch}"
|
|
||||||
"$Server_Dir/bin/clash"
|
|
||||||
)
|
|
||||||
|
|
||||||
for candidate in "${candidates[@]}"; do
|
|
||||||
if [ -x "$candidate" ]; then
|
|
||||||
echo "$candidate"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo -e "\033[31m\n[ERROR] 未找到可用的 Clash 二进制。\033[0m"
|
|
||||||
echo -e "请将对应架构的二进制放入: $Server_Dir/bin/"
|
|
||||||
echo -e "可用命名示例: clash-${resolved_arch} 或 clash-${detected_arch}"
|
|
||||||
echo -e "或通过 CLASH_BIN 指定自定义路径。"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
## 启动Clash服务
|
## 启动Clash服务
|
||||||
echo -e '\n正在启动Clash服务...'
|
echo -e '\n正在启动Clash服务...'
|
||||||
Text5="服务启动成功!"
|
Text5="服务启动成功!"
|
||||||
Text6="服务启动失败!"
|
Text6="服务启动失败!"
|
||||||
Clash_Bin=$(resolve_clash_bin)
|
Clash_Bin=$(resolve_clash_bin "$Server_Dir" "$CpuArch")
|
||||||
ReturnStatus=$?
|
ReturnStatus=$?
|
||||||
if [ $ReturnStatus -eq 0 ]; then
|
if [ $ReturnStatus -eq 0 ]; then
|
||||||
nohup "$Clash_Bin" -d "$Conf_Dir" &> "$Log_Dir/clash.log" &
|
nohup "$Clash_Bin" -d "$Conf_Dir" &> "$Log_Dir/clash.log" &
|
||||||
|
|||||||
36
uninstall.sh
Executable file
36
uninstall.sh
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
Install_Dir="${CLASH_INSTALL_DIR:-/opt/clash-for-linux}"
|
||||||
|
Service_Name="clash-for-linux"
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo -e "\033[31m[ERROR] 需要 root 权限执行卸载脚本\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
systemctl stop "${Service_Name}.service" >/dev/null 2>&1 || true
|
||||||
|
systemctl disable "${Service_Name}.service" >/dev/null 2>&1 || true
|
||||||
|
if [ -f "/etc/systemd/system/${Service_Name}.service" ]; then
|
||||||
|
rm -f "/etc/systemd/system/${Service_Name}.service"
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/etc/default/${Service_Name}" ]; then
|
||||||
|
rm -f "/etc/default/${Service_Name}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/etc/profile.d/clash-for-linux.sh" ]; then
|
||||||
|
rm -f "/etc/profile.d/clash-for-linux.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$Install_Dir" ]; then
|
||||||
|
rm -rf "$Install_Dir"
|
||||||
|
echo -e "\033[32m[OK] 已移除安装目录: ${Install_Dir}\033[0m"
|
||||||
|
else
|
||||||
|
echo -e "\033[33m[WARN] 未找到安装目录: ${Install_Dir}\033[0m"
|
||||||
|
fi
|
||||||
|
|
||||||
Reference in New Issue
Block a user