mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-03-21 22:06:45 +08:00
133 lines
2.7 KiB
Bash
133 lines
2.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
PORT_CHECK_WARNED=${PORT_CHECK_WARNED:-0}
|
|
|
|
# =========================
|
|
# 判断端口是否被占用(更稳)
|
|
# =========================
|
|
is_port_in_use() {
|
|
local port="$1"
|
|
|
|
if command -v ss >/dev/null 2>&1; then
|
|
ss -lnt 2>/dev/null | awk '{print $4}' | grep -E "[:.]${port}$" >/dev/null 2>&1
|
|
return $?
|
|
fi
|
|
|
|
if command -v netstat >/dev/null 2>&1; then
|
|
netstat -lnt 2>/dev/null | awk '{print $4}' | grep -E "[:.]${port}$" >/dev/null 2>&1
|
|
return $?
|
|
fi
|
|
|
|
if command -v lsof >/dev/null 2>&1; then
|
|
lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | awk '{print $9}' | grep -E "[:.]${port}$" >/dev/null 2>&1
|
|
return $?
|
|
fi
|
|
|
|
if [ "$PORT_CHECK_WARNED" -eq 0 ]; then
|
|
echo "[WARN] no port check tool found (ss/netstat/lsof)" >&2
|
|
PORT_CHECK_WARNED=1
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# =========================
|
|
# 找可用端口(优化版)
|
|
# =========================
|
|
find_available_port() {
|
|
local start="${1:-20000}"
|
|
local end="${2:-65000}"
|
|
local port
|
|
|
|
# 优先随机尝试
|
|
if command -v shuf >/dev/null 2>&1; then
|
|
for _ in {1..30}; do
|
|
port=$(shuf -i "${start}-${end}" -n 1)
|
|
if ! is_port_in_use "$port"; then
|
|
echo "$port"
|
|
return 0
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# fallback 顺序扫描(限制范围避免慢)
|
|
for port in $(seq "$start" "$((start + 2000))"); do
|
|
if ! is_port_in_use "$port"; then
|
|
echo "$port"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
# =========================
|
|
# 解析端口值(核心函数)
|
|
# =========================
|
|
resolve_port_value() {
|
|
local name="$1"
|
|
local value="$2"
|
|
local resolved
|
|
|
|
# auto / 空
|
|
if [ -z "$value" ] || [ "$value" = "auto" ]; then
|
|
resolved=$(find_available_port) || {
|
|
echo "[ERROR] ${name} failed to allocate port" >&2
|
|
return 1
|
|
}
|
|
echo "[WARN] ${name} auto assigned: ${resolved}" >&2
|
|
echo "$resolved"
|
|
return 0
|
|
fi
|
|
|
|
# 非数字
|
|
if ! [[ "$value" =~ ^[0-9]+$ ]]; then
|
|
echo "[ERROR] invalid port: $value" >&2
|
|
return 1
|
|
fi
|
|
|
|
# 被占用 → 自动替换
|
|
if is_port_in_use "$value"; then
|
|
resolved=$(find_available_port)
|
|
if [ -n "$resolved" ]; then
|
|
echo "[WARN] ${name} port ${value} in use, switched to ${resolved}" >&2
|
|
echo "$resolved"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
# =========================
|
|
# 解析 host:port
|
|
# =========================
|
|
resolve_host_port() {
|
|
local name="$1"
|
|
local raw="$2"
|
|
local default_host="$3"
|
|
|
|
local host
|
|
local port
|
|
|
|
if [ -z "$raw" ] || [ "$raw" = "auto" ]; then
|
|
host="$default_host"
|
|
port="auto"
|
|
else
|
|
if [[ "$raw" == *:* ]]; then
|
|
host="${raw%:*}"
|
|
port="${raw##*:}"
|
|
else
|
|
host="$default_host"
|
|
port="$raw"
|
|
fi
|
|
fi
|
|
|
|
# host 兜底
|
|
[ -z "$host" ] && host="$default_host"
|
|
|
|
port=$(resolve_port_value "$name" "$port") || return 1
|
|
|
|
echo "${host}:${port}"
|
|
} |