#!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH GREEN='\033[32m' YELLOW='\033[33m' RED='\033[31m' NC='\033[0m' DEFAULT_SOCKS_PORT=20000 DEFAULT_VISION_PORT=22000 DEFAULT_XHTTP_PORT=24000 DEFAULT_VMESS_PORT=26000 DEFAULT_UUID="2a67e495-5387-4322-9596-724009292852" DEFAULT_REALITY_DEST="www.sky.com:443" DEFAULT_SOCKS_USERNAME="ads" DEFAULT_SOCKS_PASSWORD="adspasswd" DEFAULT_SHORT_ID="6ba85179e30d4fc2" DEFAULT_XHTTP_PATH="/pQ93NcbxmmoYcl7U7cppCJpmtGMZUhXFqQA" DEFAULT_XHTTP_SPX="/" XRAY_BIN="/usr/local/bin/xrayL" CONFIG_DIR="/etc/xray-batch" CONFIG_PATH="${CONFIG_DIR}/config.json" SERVICE_NAME="xray-batch" IP_ADDRESSES=($(hostname -I)) PROTOCOLS=() SOCKS_PORT=$DEFAULT_SOCKS_PORT VISION_PORT=$DEFAULT_VISION_PORT XHTTP_PORT=$DEFAULT_XHTTP_PORT VMESS_PORT=$DEFAULT_VMESS_PORT UUID=$DEFAULT_UUID REALITY_DEST=$DEFAULT_REALITY_DEST SOCKS_USERNAME=$DEFAULT_SOCKS_USERNAME SOCKS_PASSWORD=$DEFAULT_SOCKS_PASSWORD XHTTP_PATH=$DEFAULT_XHTTP_PATH XHTTP_SPX=$DEFAULT_XHTTP_SPX REALITY_PRIVATE_KEY="" REALITY_PUBLIC_KEY="" usage() { echo "用法: $0 [选项]" echo " $0 --cleanup" echo "选项:" echo " --cleanup 清理部署配置、服务、UFW 规则" echo " --uninstall 卸载(清理 + 删除二进制 + 生成文件)" echo " --socks-port PORT SOCKS 起始端口 (默认 $DEFAULT_SOCKS_PORT)" echo " --vision-port PORT Vision 起始端口 (默认 $DEFAULT_VISION_PORT)" echo " --xhttp-port PORT XHTTP 起始端口 (默认 $DEFAULT_XHTTP_PORT)" echo " --vmess-port PORT VMess 起始端口 (默认 $DEFAULT_VMESS_PORT)" echo " -p, --port PORT 单协议模式起始端口" echo " -u, --uuid UUID UUID (默认 $DEFAULT_UUID)" echo " -d, --dest HOST REALITY 目标域名 (默认 $DEFAULT_REALITY_DEST)" echo " --socks-user USER SOCKS 用户名 (默认 $DEFAULT_SOCKS_USERNAME)" echo " --socks-pass PASS SOCKS 密码 (默认 $DEFAULT_SOCKS_PASSWORD)" echo " --xhttp-path PATH XHTTP 路径 (默认 $DEFAULT_XHTTP_PATH)" echo " --spx PATH XHTTP spx (默认 $DEFAULT_XHTTP_SPX)" echo "示例:" echo " $0 all --socks-port 20000 --vision-port 22000 --xhttp-port 24000 --vmess-port 26000" echo " $0 socks,vision --socks-port 20000 --vision-port 22000" echo " $0 vmess --port 26000" exit 0 } _error() { echo -e "${RED}[错误]${NC} $*" >&2 exit 1 } _info() { echo -e "${GREEN}[信息]${NC} $*" } _warn() { echo -e "${YELLOW}[警告]${NC} $*" } _os() { if [[ -f "/etc/debian_version" ]]; then source /etc/os-release && printf -- "%s" "${ID}" && return fi if [[ -f "/etc/redhat-release" ]]; then printf -- "%s" "centos" && return fi } _os_ver() { local ver="" if [[ -f /etc/os-release ]]; then source /etc/os-release ver="${VERSION_ID%%.*}" fi printf -- "%s" "${ver}" } check_os() { local os=$(_os) case "$os" in centos) [[ "$(_os_ver)" -lt 7 ]] && _error "不支持 CentOS < 7" ;; ubuntu) [[ "$(_os_ver)" -lt 16 ]] && _error "不支持 Ubuntu < 16" ;; debian) [[ "$(_os_ver)" -lt 9 ]] && _error "不支持 Debian < 9" ;; *) _error "不支持当前系统 (centos/ubuntu/debian 仅支持)" ;; esac } cmd_exists() { if eval type type >/dev/null 2>&1; then eval type "$1" >/dev/null 2>&1 elif command >/dev/null 2>&1; then command -v "$1" >/dev/null 2>&1 else which "$1" >/dev/null 2>&1 fi } install_deps() { _info "安装系统依赖..." case "$(_os)" in centos) if cmd_exists "dnf"; then dnf install -y curl wget unzip jq openssl ca-certificates qrencode else yum install -y epel-release && yum install -y curl wget unzip jq openssl ca-certificates qrencode fi ;; ubuntu|debian) apt update -y && apt install -y curl wget unzip jq openssl ca-certificates qrencode ;; esac } install_xray() { _info "安装 Xray-core ..." case "$(_os)" in centos) cmd_exists "unzip" || yum install -y unzip ;; ubuntu|debian) cmd_exists "unzip" || apt install -y unzip ;; esac wget -q https://github.com/XTLS/Xray-core/releases/download/v25.12.1/Xray-linux-64.zip -O /tmp/xray.zip unzip -qo /tmp/xray.zip -d /tmp/xray && mv /tmp/xray/xray "$XRAY_BIN" chmod +x "$XRAY_BIN" rm -rf /tmp/xray.zip /tmp/xray _info "Xray 已安装到 $XRAY_BIN" } generate_reality_keys() { local key_file="${CONFIG_DIR}/reality_key" if [ -f "$key_file" ]; then REALITY_PRIVATE_KEY=$(sed -n '1p' "$key_file") REALITY_PUBLIC_KEY=$(sed -n '2p' "$key_file") _info "使用已保存的 REALITY 密钥对" return fi local keys=$("$XRAY_BIN" x25519) REALITY_PRIVATE_KEY=$(echo "$keys" | grep -i 'private' | awk '{print $NF}') REALITY_PUBLIC_KEY=$(echo "$keys" | grep -iE 'public|password' | awk '{print $NF}') if [ -z "$REALITY_PRIVATE_KEY" ] || [ -z "$REALITY_PUBLIC_KEY" ]; then _error "无法生成 REALITY 密钥对" fi mkdir -p "$CONFIG_DIR" printf "%s\n%s" "$REALITY_PRIVATE_KEY" "$REALITY_PUBLIC_KEY" > "$key_file" _info "REALITY 密钥对已保存: $key_file" } generate_config() { local total_ports=${#IP_ADDRESSES[@]} _info "IP 数量: $total_ports" local has_reality=0 for p in "${PROTOCOLS[@]}"; do case "$p" in socks) ;; *) has_reality=1 ;; esac done if [ "$has_reality" -eq 1 ]; then _info "生成 REALITY 密钥对..." generate_reality_keys fi mkdir -p "$CONFIG_DIR" local data="" for p in "${PROTOCOLS[@]}"; do local sp case "$p" in socks) sp=$SOCKS_PORT ;; vision) sp=$VISION_PORT ;; xhttp) sp=$XHTTP_PORT ;; vmess) sp=$VMESS_PORT ;; esac for ((i = 0; i < total_ports; i++)); do data+="${p}|${i}|${IP_ADDRESSES[$i]}|$((sp + i))"$'\n' done done local sni="${REALITY_DEST%:*}" local config config=$(echo "$data" | jq -Rn \ --arg uuid "$UUID" \ --arg dest "$REALITY_DEST" \ --arg sni "$sni" \ --arg privkey "$REALITY_PRIVATE_KEY" \ --arg sid "$DEFAULT_SHORT_ID" \ --arg spx "$XHTTP_SPX" \ --arg xpath "$XHTTP_PATH" \ --arg user "$SOCKS_USERNAME" \ --arg pass "$SOCKS_PASSWORD" \ '[ inputs | select(length > 0) | split("|") | {proto: .[0], idx: .[1], ip: .[2], port: (.[3]|tonumber)} ] | { log: {loglevel: "none"}, inbounds: map( if .proto == "socks" then { tag: ("socks_in_" + .idx), listen: "0.0.0.0", port: .port, protocol: "socks", settings: {auth: "password", udp: false, accounts: [{user: $user, pass: $pass}]} } elif .proto == "vision" then { tag: ("vision_in_" + .idx), listen: "0.0.0.0", port: .port, protocol: "vless", settings: {decryption: "none", clients: [{id: $uuid, email: "user@vision.reality", flow: "xtls-rprx-vision"}]}, streamSettings: { network: "tcp", security: "reality", realitySettings: {show: false, dest: $dest, serverNames: [$sni], privateKey: $privkey, shortIds: [$sid]} } } elif .proto == "xhttp" then { tag: ("xhttp_in_" + .idx), listen: "0.0.0.0", port: .port, protocol: "vless", settings: {decryption: "none", clients: [{id: $uuid, email: "user@xhttp.reality"}]}, streamSettings: { network: "xhttp", security: "reality", realitySettings: {show: false, dest: $dest, serverNames: [$sni], privateKey: $privkey, shortIds: [$sid], spx: $spx}, xhttpSettings: {host: "", path: $xpath, mode: "auto"} } } else { tag: ("vmess_in_" + .idx), listen: "0.0.0.0", port: .port, protocol: "vmess", settings: {clients: [{id: $uuid, security: "auto"}]}, streamSettings: { network: "tcp", security: "reality", realitySettings: {show: false, dest: $dest, serverNames: [$sni], privateKey: $privkey, shortIds: [$sid]} } } end ), outbounds: map({ tag: (.proto + "_out_" + .idx), sendThrough: .ip, protocol: "freedom" }), routing: {rules: map({ type: "field", inboundTag: [(.proto + "_in_" + .idx)], outboundTag: (.proto + "_out_" + .idx) })} }') echo "$config" >"$CONFIG_PATH" _info "配置已写入: $CONFIG_PATH" } setup_service() { cat >"/etc/systemd/system/${SERVICE_NAME}.service" </dev/null || { _warn "UFW 未安装,跳过防火墙配置。"; return; } for p in "${PROTOCOLS[@]}"; do local sp case "$p" in socks) sp=$SOCKS_PORT ;; vision) sp=$VISION_PORT ;; xhttp) sp=$XHTTP_PORT ;; vmess) sp=$VMESS_PORT ;; esac local ep=$((sp + ${#IP_ADDRESSES[@]} - 1)) _info "配置 UFW 防火墙 ($p): $sp-$ep" ufw delete allow "$sp:$ep/tcp" 2>/dev/null || true ufw allow "$sp:$ep/tcp" if [ "$p" == "socks" ]; then ufw allow "$sp:$ep/udp" fi done ufw reload } generate_connections() { local ipv4=$(hostname -I | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1) local conn_ip="${ipv4:-${IP_ADDRESSES[0]}}" local sni="${REALITY_DEST%:*}" local spx_enc=$(echo -n "$XHTTP_SPX" | jq -sRr @uri) local path_enc=$(echo -n "$XHTTP_PATH" | jq -sRr @uri) for p in "${PROTOCOLS[@]}"; do local sp case "$p" in socks) sp=$SOCKS_PORT ;; vision) sp=$VISION_PORT ;; xhttp) sp=$XHTTP_PORT ;; vmess) sp=$VMESS_PORT ;; esac local conn_file="${p}_connections.txt" > "$conn_file" for ((i = 0; i < ${#IP_ADDRESSES[@]}; i++)); do local port=$((sp + i)) case "$p" in socks) echo "${conn_ip}:${port}:${SOCKS_USERNAME}:${SOCKS_PASSWORD}{${port}}" ;; vision) printf 'vless://%s@%s:%s?encryption=none&security=reality&sni=%s&fp=chrome&pbk=%s&sid=%s&type=tcp&flow=xtls-rprx-vision#%s\n' \ "$UUID" "$conn_ip" "$port" "$sni" "$REALITY_PUBLIC_KEY" "$DEFAULT_SHORT_ID" "$port" ;; xhttp) printf 'vless://%s@%s:%s?encryption=none&security=reality&sni=%s&fp=chrome&pbk=%s&sid=%s&spx=%s&type=xhttp&path=%s#%s\n' \ "$UUID" "$conn_ip" "$port" "$sni" "$REALITY_PUBLIC_KEY" "$DEFAULT_SHORT_ID" "$spx_enc" "$path_enc" "$port" ;; vmess) vmess_b64=$(printf '{"v":"2","ps":"%s","add":"%s","port":"%s","id":"%s","aid":"0","scy":"auto","net":"tcp","type":"none","tls":"reality","sni":"%s","fp":"chrome","pbk":"%s","sid":"%s"}' \ "$port" "$conn_ip" "$port" "$UUID" "$sni" "$REALITY_PUBLIC_KEY" "$DEFAULT_SHORT_ID" | base64 -w0) echo "vmess://${vmess_b64}" ;; esac done > "$conn_file" _info "连接信息已保存到: $conn_file" echo "连接示例(前 3 个):" head -3 "$conn_file" | while read -r line; do echo " $line"; done echo "" done } generate_clash_yaml() { local clash_file="clash.yaml" local ipv4=$(hostname -I | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1) local conn_ip="${ipv4:-${IP_ADDRESSES[0]}}" local sni="${REALITY_DEST%:*}" { echo "port: 7890" echo "socks-port: 7891" echo "mode: rule" echo "log-level: info" echo "allow-lan: true" echo "mixed-port: 7892" echo "external-controller: 127.0.0.1:9090" echo "" echo "proxies:" for p in "${PROTOCOLS[@]}"; do [ "$p" = "socks" ] && continue local sp case "$p" in vision) sp=$VISION_PORT ;; xhttp) sp=$XHTTP_PORT ;; vmess) sp=$VMESS_PORT ;; esac for ((i = 0; i < ${#IP_ADDRESSES[@]}; i++)); do local port=$((sp + i)) case "$p" in vision) echo " - name: \"vision_${port}\"" echo " type: vless" echo " server: ${conn_ip}" echo " port: ${port}" echo " uuid: ${UUID}" echo " network: tcp" echo " tls: true" echo " servername: ${sni}" echo " flow: xtls-rprx-vision" echo " reality-opts:" echo " public-key: ${REALITY_PUBLIC_KEY}" echo " short-id: ${DEFAULT_SHORT_ID}" echo " client-fingerprint: chrome" ;; xhttp) echo " - name: \"xhttp_${port}\"" echo " type: vless" echo " server: ${conn_ip}" echo " port: ${port}" echo " uuid: ${UUID}" echo " network: xhttp" echo " tls: true" echo " servername: ${sni}" echo " reality-opts:" echo " public-key: ${REALITY_PUBLIC_KEY}" echo " short-id: ${DEFAULT_SHORT_ID}" echo " http-opts:" echo " path: ${XHTTP_PATH}" echo " client-fingerprint: chrome" ;; vmess) echo " - name: \"vmess_${port}\"" echo " type: vmess" echo " server: ${conn_ip}" echo " port: ${port}" echo " uuid: ${UUID}" echo " alterId: 0" echo " cipher: auto" echo " network: tcp" echo " tls: true" echo " servername: ${sni}" echo " reality-opts:" echo " public-key: ${REALITY_PUBLIC_KEY}" echo " short-id: ${DEFAULT_SHORT_ID}" echo " client-fingerprint: chrome" ;; esac done done echo "" echo "proxy-groups:" echo " - name: Proxy" echo " type: select" echo " proxies:" for p in "${PROTOCOLS[@]}"; do [ "$p" = "socks" ] && continue echo " - \"${p}-pool\"" done for p in "${PROTOCOLS[@]}"; do [ "$p" = "socks" ] && continue local sp case "$p" in vision) sp=$VISION_PORT ;; xhttp) sp=$XHTTP_PORT ;; vmess) sp=$VMESS_PORT ;; esac echo " - name: ${p}-pool" echo " type: load-balance" echo " strategy: round-robin" echo " proxies:" for ((i = 0; i < ${#IP_ADDRESSES[@]}; i++)); do local port=$((sp + i)) echo " - \"${p}_${port}\"" done done echo "" echo "rules:" echo " - MATCH,Proxy" } > "$clash_file" _info "Clash 配置文件已保存到: $clash_file" } generate_subscriptions() { local has_vless=0 has_vmess=0 for p in "${PROTOCOLS[@]}"; do case "$p" in vision|xhttp) has_vless=1 ;; vmess) has_vmess=1 ;; esac done if [ "$has_vless" -eq 1 ]; then { [ -f vision_connections.txt ] && cat vision_connections.txt [ -f xhttp_connections.txt ] && cat xhttp_connections.txt } | base64 -w0 > vless_sub.txt _info "VLESS 订阅文件已保存到: vless_sub.txt" fi if [ "$has_vmess" -eq 1 ]; then base64 -w0 < vmess_connections.txt > vmess_sub.txt _info "VMess 订阅文件已保存到: vmess_sub.txt" fi } cleanup() { _info "清理 Xray 批量部署..." systemctl stop "${SERVICE_NAME}.service" 2>/dev/null || true systemctl disable "${SERVICE_NAME}.service" 2>/dev/null || true rm -f "/etc/systemd/system/${SERVICE_NAME}.service" systemctl daemon-reload if [ -f "$CONFIG_PATH" ] && command -v ufw &>/dev/null; then _info "删除 UFW 规则..." local protocols protocols=$(jq -r '[.inbounds[].tag | split("_")[0]] | unique[]' "$CONFIG_PATH" 2>/dev/null) for p in $protocols; do local range range=$(jq -r "[.inbounds[] | select(.tag | startswith(\"${p}\")) | .port] | \"\(min):\(max)\"" "$CONFIG_PATH" 2>/dev/null) [ -z "$range" ] || [ "$range" = "null-null" ] && continue ufw delete allow "${range}/tcp" 2>/dev/null || true [ "$p" = "socks" ] && ufw delete allow "${range}/udp" 2>/dev/null || true done ufw reload 2>/dev/null || true fi rm -rf "$CONFIG_DIR" _info "已删除: $CONFIG_DIR" _info "清理完成" exit 0 } uninstall() { _info "卸载 Xray(清理所有组件)..." systemctl stop "${SERVICE_NAME}.service" 2>/dev/null || true systemctl disable "${SERVICE_NAME}.service" 2>/dev/null || true rm -f "/etc/systemd/system/${SERVICE_NAME}.service" systemctl daemon-reload if [ -f "$CONFIG_PATH" ] && command -v ufw &>/dev/null; then _info "删除 UFW 规则..." local protocols protocols=$(jq -r '[.inbounds[].tag | split("_")[0]] | unique[]' "$CONFIG_PATH" 2>/dev/null) for p in $protocols; do local range range=$(jq -r "[.inbounds[] | select(.tag | startswith(\"${p}\")) | .port] | \"\(min):\(max)\"" "$CONFIG_PATH" 2>/dev/null) [ -z "$range" ] || [ "$range" = "null-null" ] && continue ufw delete allow "${range}/tcp" 2>/dev/null || true [ "$p" = "socks" ] && ufw delete allow "${range}/udp" 2>/dev/null || true done ufw reload 2>/dev/null || true fi rm -rf "$CONFIG_DIR" rm -f "$XRAY_BIN" rm -f socks_connections.txt vision_connections.txt xhttp_connections.txt vmess_connections.txt rm -f clash.yaml vless_sub.txt vmess_sub.txt _info "已删除: $CONFIG_DIR" _info "已删除: $XRAY_BIN" _info "卸载完成" exit 0 } show_status() { if ! command -v jq &>/dev/null; then _warn "jq 未安装, 无法读取配置" return fi if [ ! -f "$CONFIG_PATH" ]; then _warn "未检测到部署配置 ($CONFIG_PATH)" return fi local protocols protocols=$(jq -r '[.inbounds[].tag | split("_")[0]] | unique[]' "$CONFIG_PATH" 2>/dev/null) [ -z "$protocols" ] && return echo "----------------------------------------------" echo " 当前部署状态:" for p in $protocols; do local range range=$(jq -r "[.inbounds[] | select(.tag | startswith(\"${p}\")) | .port] | \"\(min)-\(max)\"" "$CONFIG_PATH" 2>/dev/null) local count count=$(jq -r "[.inbounds[] | select(.tag | startswith(\"${p}\"))] | length" "$CONFIG_PATH" 2>/dev/null) echo " $p: 端口 $range ($count 个)" done if systemctl is-active "${SERVICE_NAME}.service" &>/dev/null; then echo " 服务状态: ${GREEN}运行中${NC}" else echo " 服务状态: ${RED}未运行${NC}" fi echo "----------------------------------------------" } show_menu() { while true; do command -v clear &>/dev/null && clear || printf "\033c" echo "==============================================" echo " Xray 批量部署管理" echo "==============================================" show_status echo "" echo " 1) 部署全部协议 (socks + vision + xhttp + vmess)" echo " 2) 部署 SOCKS5" echo " 3) 部署 VLESS+Vision+REALITY" echo " 4) 部署 VLESS+XHTTP+REALITY" echo " 5) 部署 VMess+TCP" echo " 6) 部署自定义组合" echo " 7) 清理部署配置" echo " 8) 卸载(删除配置 + 二进制 + 生成文件)" echo " 0) 退出" echo "==============================================" printf " 请输入选项 [0-8]: " read -r opt case "$opt" in 1) PROTOCOLS=(socks vision xhttp vmess); break ;; 2) PROTOCOLS=(socks); break ;; 3) PROTOCOLS=(vision); break ;; 4) PROTOCOLS=(xhttp); break ;; 5) PROTOCOLS=(vmess); break ;; 6) printf " 输入协议组合 (用逗号分隔, 如 socks,vision): " read -r combo IFS=',' read -ra PROTOCOLS <<< "$combo" break ;; 7) cleanup ;; 8) uninstall ;; 0) echo "退出"; exit 0 ;; *) _warn "无效选项, 请重试"; sleep 1 ;; esac done } parse_args() { if [ $# -eq 0 ]; then show_menu return fi local first="$1" case "$first" in --cleanup) cleanup ;; --uninstall) uninstall ;; -h|--help) usage ;; all) PROTOCOLS=(socks vision xhttp vmess) shift ;; socks|vision|xhttp|vmess) PROTOCOLS=("$first") shift ;; *) # 逗号分隔: socks,vision,xhttp,vmess if echo "$first" | grep -q ','; then IFS=',' read -ra PROTOCOLS <<< "$first" shift else _error "无效协议: $first (可用: socks, vision, xhttp, vmess, all)" fi ;; esac while [[ $# -gt 0 ]]; do case "$1" in --socks-port) SOCKS_PORT="$2"; shift 2 ;; --vision-port) VISION_PORT="$2"; shift 2 ;; --xhttp-port) XHTTP_PORT="$2"; shift 2 ;; --vmess-port) VMESS_PORT="$2"; shift 2 ;; -p|--port) # 单协议模式: 设置对应协议的端口 case "${PROTOCOLS[0]}" in socks) SOCKS_PORT="$2" ;; vision) VISION_PORT="$2" ;; xhttp) XHTTP_PORT="$2" ;; vmess) VMESS_PORT="$2" ;; esac shift 2 ;; -u|--uuid) UUID="$2"; shift 2 ;; -d|--dest) REALITY_DEST="$2"; shift 2 ;; --socks-user) SOCKS_USERNAME="$2"; shift 2 ;; --socks-pass) SOCKS_PASSWORD="$2"; shift 2 ;; --xhttp-path) XHTTP_PATH="$2"; shift 2 ;; --spx) XHTTP_SPX="$2"; shift 2 ;; -h|--help) usage ;; *) _error "未知参数: $1" ;; esac done if [ ${#IP_ADDRESSES[@]} -eq 0 ]; then _error "未检测到 IP 地址" fi } main() { parse_args "$@" [[ $EUID -ne 0 ]] && _error "请以 root 权限运行" check_os [[ ! -x "$XRAY_BIN" ]] && install_xray cmd_exists "jq" || install_deps generate_config open_firewall setup_service generate_connections generate_clash_yaml generate_subscriptions echo "==============================================" echo " 部署完成" for p in "${PROTOCOLS[@]}"; do local sp case "$p" in socks) sp=$SOCKS_PORT ;; vision) sp=$VISION_PORT ;; xhttp) sp=$XHTTP_PORT ;; vmess) sp=$VMESS_PORT ;; esac echo " $p: $sp - $((sp + ${#IP_ADDRESSES[@]} - 1)) (${#IP_ADDRESSES[@]} 端口)" done if [ "${#PROTOCOLS[@]}" -eq 1 ] && [ "${PROTOCOLS[0]}" != "socks" ]; then echo " REALITY 目标: $REALITY_DEST" echo " REALITY 公钥: $REALITY_PUBLIC_KEY" fi if echo "${PROTOCOLS[@]}" | grep -q 'socks'; then echo " SOCKS 账号: $SOCKS_USERNAME / $SOCKS_PASSWORD" fi echo "==============================================" } main "$@"