Skip to content

安装 v2ray

Ubuntu 安装 v2ray

(弃用)apt 安装

apt 安装

Ubuntu 22.04 下安装的版本 4.34.0 有问题,例如用 curl --proxy 无法正常连接到代理端口

sh
sudo apt install v2ray

下列文件将被安装:

  • /usr/bin/v2ray/v2ray: V2Ray executable
  • /usr/bin/v2ray/v2ctl: Utility
  • /etc/v2ray/config.json: Config file
  • /usr/bin/v2ray/geoip.dat: IP data file
  • /usr/bin/v2ray/geosite.dat: domain data file
sh
# 如果已经这样装了,请卸载
sudo apt autoremove v2ray

See: v2ray 4.34.0-5 in ubuntu22.04 bug · Issue #3005 · v2ray/v2ray-core

Did you install V2Ray via the sudo apt install v2ray command? I think this V2Ray 4.34.0 version is NOT stable.

When I downgraded to V2Ray 4.28.2, the problem was solved.

See: v2ray - Ubuntu PPA

(弃用)解压缩安装

解压缩安装

根本无法启动

sh
wget https://githubfast.com/v2ray/v2ray-core/releases/download/v4.28.2/v2ray-linux-64.zip -O v2ray-linux-64.zip
unzip v2ray-linux-64.zip -d /usr/bin/v2ray
# add to path
export PATH=$PATH:/usr/bin/v2ray

脚本安装

修改后的脚本附在文末: v2ray 完整安装脚本

简单修改

直接拿下面这个脚本安装是不行的,因为 github.com 被墙了

所以需要修改几个地方:

  1. 把脚本中的 github.com 替换为 githubfast.com,这是一个国内能访问的 github 镜像

    sh
    download_v2ray() {
      DOWNLOAD_LINK="https://githubfast.com/v2fly/v2ray-core/releases/download/$RELEASE_VERSION/v2ray-linux-$MACHINE.zip"
      ...
    }
  2. 获取最新版本时需要访问 api.github.com,但这个接口没有对应的 githubfast 镜像,所以需要注释掉联网部分,手动指定版本号 v4.28.2

    sh
    #   # Get V2Ray release version number
    #   TMP_FILE="$(mktemp)"
    #   if ! curl -x "${PROXY}" -sS -i -H "Accept: application/vnd.github.v3+json" -o "$TMP_FILE" 'https://api.github.com/repos/v2fly/v2ray-core/releases/latest'; then
    #     "rm" "$TMP_FILE"
    #     echo 'error: Failed to get release list, please check your network.'
    #     exit 1
    #   fi
    #   HTTP_STATUS_CODE=$(awk 'NR==1 {print $2}' "$TMP_FILE")
    #   if [[ $HTTP_STATUS_CODE -lt 200 ]] || [[ $HTTP_STATUS_CODE -gt 299 ]]; then
    #     "rm" "$TMP_FILE"
    #     echo "error: Failed to get release list, GitHub API response code: $HTTP_STATUS_CODE"
    #     exit 1
    #   fi
    #   RELEASE_LATEST="$(sed 'y/,/\n/' "$TMP_FILE" | grep 'tag_name' | awk -F '"' '{print $4}')"
    #   "rm" "$TMP_FILE"
    #   RELEASE_VERSION="v${RELEASE_LATEST#v}"
        RELEASE_VERSION="v4.28.2" # <--- 添加这里
  3. download_v2ray() 函数中要下载验证文件,但是 .dsgt 文件在 githubfast.com 中也没有,所以要注释掉这部分:

    sh
    download_v2ray() {
      DOWNLOAD_LINK="https://githubfast.com/v2fly/v2ray-core/releases/download/$RELEASE_VERSION/v2ray-linux-$MACHINE.zip"
      echo "Downloading V2Ray archive: $DOWNLOAD_LINK"
      if ! curl -x "${PROXY}" -R -H 'Cache-Control: no-cache' -o "$ZIP_FILE" "$DOWNLOAD_LINK"; then
        echo 'error: Download failed! Please check your network or try again.'
        return 1
      fi
    #   echo "Downloading verification file for V2Ray archive: $DOWNLOAD_LINK.dgst"
    #   if ! curl -x "${PROXY}" -sSR -H 'Cache-Control: no-cache' -o "$ZIP_FILE.dgst" "$DOWNLOAD_LINK.dgst"; then
    #     echo 'error: Download failed! Please check your network or try again.'
    #     return 1
    #   fi
    #   if [[ "$(cat "$ZIP_FILE".dgst)" == 'Not Found' ]]; then
    #     echo 'error: This version does not support verification. Please replace with another version.'
    #     return 1
    #   fi
    
    #   # Verification of V2Ray archive
    #   CHECKSUM=$(awk -F '= ' '/256=/ {print $2}' < "${ZIP_FILE}.dgst")
    #   LOCALSUM=$(sha256sum "$ZIP_FILE" | awk '{printf $1}')
    #   if [[ "$CHECKSUM" != "$LOCALSUM" ]]; then
    #     echo 'error: SHA256 check failed! Please check your network or try again.'
    #     return 1
    #   fi
    }

一键安装

其实就是下载和运行上面修改后的脚本:

sh
wget https://raw.staticdn.net/Hansimov/blog/main/docs/notes/scripts/v2ray-install-release.sh -O ./v2ray-install-release.sh && chmod +x ./v2ray-install-release.sh && sudo ./v2ray-install-release.sh

如果局域网内其他设备有这个安装脚本,可以直接下载:

sh
cd ~/downloads
scp asimov@[host]:/home/asimov/repos/blog/docs/notes/scripts/v2ray-install-release.sh ./
chmod +x ./v2ray-install-release.sh && sudo ./v2ray-install-release.sh

下载 geoip 和 geosite

类似上面的,也需要把 github.com 替换为 githubfast.com

sh
sudo wget https://githubfast.com/v2fly/geoip/releases/latest/download/geoip.dat -O /usr/local/share/v2ray/geoip.dat
sudo wget https://githubfast.com/v2fly/domain-list-community/releases/latest/download/dlc.dat -O /usr/local/share/v2ray/geosite.dat

Windows 安装 v2ray

下载 release:

解压,参考下面的样例修改 config.json,然后运行 v2ray.exe

或者创建 launch_v2ray.bat 文件,内容如下。双击启动:

sh
v2ray.exe run --config=config.json

若要开机自启,创建快捷方式,发送到下面路径即可:

  • C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
  • 又名:C:\ProgramData\Microsoft\Windows\[开始]菜单\程序\启动

配置 server 和 client

配置 server 的 X-UI

在远端代理服务器安装 x-ui:

sh
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)

安装时会提示设置 port。注意需要在防火墙中开放该端口。同时还会生成 Username 和 Password。输出形如:

sh
Username: **********
Password: **********
Port: 9999
WebBasePath: ******************
Access URL: http://XXX.XXX.XXX.XXX:9999/******************

查看设置:

sh
x-ui settings

输出形如:

sh
The OS release is: ubuntu
[INF] current panel settings as follows:
Warning: Panel is not secure with SSL
hasDefaultCredential: false
port: 9999
webBasePath: /******************/
Access URL: http://XXX.XXX.XXX.XXX:9999/******************/

访问 Access URL,输入之前命令行的 UsernamePassword,进入 x-ui 的 dashboard。

在入站列表添加一个 vmess 节点。

这几个信息后面会用到:address, port, users (id, alterId)。

配置 client 的 config.json

完整的样例附在文末:config.json 完整样例

v2ray 默认调用的配置文件位于:

  • /usr/local/etc/v2ray/config.json

需要修改配置文件中的:

  • inboundssockshttpport
  • outbounds: vnext > address, port, users (id, alterId)

如果局域网内其他设备有这个配置文件,可以直接下载:

sh
scp asimov@[host]:/usr/local/etc/v2ray/config.json /usr/local/etc/v2ray/
# scp asimov@[host]:/usr/local/etc/v2ray/new.json /usr/local/etc/v2ray/

See: Client Configuration - V2Fly.org

运行 client

sh
sudo systemctl enable v2ray
sudo systemctl start v2ray

显示服务状态:

sh
sudo systemctl status v2ray

测试代理:

sh
curl --proxy http://127.0.0.1:11111 http://ifconfig.me/ip && echo ""

查看日志:

sh
journalctl -u v2ray.service

See: systemd - How to see full log from systemctl status service? - Unix & Linux Stack Exchange

运行多个 v2ray 服务

假如想要添加的新服务对应的配置文件为 new.json。同时不想改动原有的 v2ray 的服务,这时可以采用 v2ray@service 这个模板单元。

sh
cat /etc/systemd/system/v2ray@.service
sh
[Unit]
Description=V2Ray Service
Documentation=https://www.v2fly.org/
After=network.target nss-lookup.target

[Service]
User=nobody
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/usr/local/bin/v2ray -config /usr/local/etc/v2ray/%i.json
Restart=on-failure
RestartPreventExitStatus=23

[Install]
WantedBy=multi-user.target
sh
sudo cp /usr/local/etc/v2ray/config.json /usr/local/etc/v2ray/new.json
sudo nano /usr/local/etc/v2ray/new.json

修改 new.json 中的对应内容:

  • inbounds: port (socks + http)
  • outbounds: address, port, id

如果局域网内其他设备有这个配置文件,可以直接下载:

sh
scp asimov@[host]:/usr/local/etc/v2ray/new.json /usr/local/etc/v2ray/

重载 systemd 配置:

sh
sudo systemctl daemon-reload

设置开机自启,并启动:

sh
sudo systemctl enable v2ray@new
sudo systemctl start v2ray@new

查看服务状态:

sh
sudo systemctl status v2ray@new

测试新代理:

sh
curl --proxy http://127.0.0.1:11119 http://ifconfig.me/ip && echo ""

在直连与本机前置代理间切换

如果 11119 对应的远端节点只能经本机 11111 访问,可以在其出站项使用 proxySettings.tag 指向一个 127.0.0.1:11111 的 HTTP outbound。两种模式分别是:

  • direct:应用 → 本机 11119 → 原远端节点 → 目标网站。
  • relay:应用 → 本机 11119 → 本机 11111 的远端节点 → 原远端节点 → 目标网站。

这里的 direct 仍使用 11119 原有的远端代理和认证,只是取消前置中转; 不会把所有请求改成 freedom,也不会清除已有的分流规则。

在 VM 中安装切换脚本:

sh
sudo install -m 755 v2ray_11119_route.py /usr/local/sbin/v2ray-11119-route
sh
# 查看当前模式
sudo v2ray-11119-route status

# 独立测试直连,不修改生产配置
sudo v2ray-11119-route test-direct

# 测试成功后切换为直连
sudo v2ray-11119-route direct

# 切换回经 11111 中转
sudo v2ray-11119-route relay

脚本默认读取 /usr/local/etc/v2ray/new.json,管理 v2ray@new.service, 从原配置保留远端地址、端口和凭据。测试进程使用服务自身的 Unix 用户、独立的 loopback 临时端口,并强制探测请求经过所选代理,防止分流规则造成误判。 只有两轮 HTTPS 检查全部通过才应用配置;切换后的服务或请求检查失败会恢复 原配置并重启服务。已处于目标模式时,测试通过后不会重复重启。

原配置备份保存在 VM 内 /var/backups/v2ray-route/,权限为 0600,目录为 0700。备份包含真实代理凭据,不能提交到 public 仓库;仓库中的脚本不包含 真实上游地址或认证信息。依赖 Python 3.9+、curl、systemd 和现有 V2Ray 4.x。

这份脚本针对现有 V2Ray 4.28 的普通 TCP 配置。若使用 WebSocket/TLS 或更新核心, 应重新核对传输层中转行为;transportLayer 从 4.35 才加入,不能直接套到旧版。 参见 V2Fly 出站代理配置

2026-09-05 在 ai122 的复测中,原上游 TCP 端口能够连接,但完整直连请求仍出现 长延迟和超时;延长等待后一个请求约 26 秒才成功,另一个仍超时。经 11111 中转的四次请求均成功,约 0.8–3.1 秒,因此保留中转。不能仅凭 TCP 端口恢复 连接就自动切回直连,也不能据此断定具体是哪一家运营商或哪一跳的问题。

py
#!/usr/bin/env python3
"""Switch an existing V2Ray 4.x TCP outbound between direct and local relay.

Run inside the VM. Reads the existing private config; no upstream credentials
are embedded here. Candidate probes use a separate loopback listener and the
service's Unix account. Failed changes restore the previous config and service.
"""
import argparse
import copy
import concurrent.futures
import datetime
import fcntl
import grp
import json
import os
from pathlib import Path
import pwd
import socket
import stat
import subprocess
import tempfile
import time


RELAY_TAG = "via-local-v2ray-http-11111"
CHECKS = (
    ("https://www.google.com/generate_204", "204"),
    ("https://www.cloudflare.com/cdn-cgi/trace", "200"),
)


def command(argv, timeout=35):
    return subprocess.run(argv, capture_output=True, text=True, timeout=timeout,
                          stdin=subprocess.DEVNULL)


def service_account(service):
    user = command(["systemctl", "show", service, "-p", "User", "--value"]).stdout.strip()
    group = command(["systemctl", "show", service, "-p", "Group", "--value"]).stdout.strip()
    account = pwd.getpwnam(user or "root")
    gid = grp.getgrnam(group).gr_gid if group else account.pw_gid
    return account.pw_uid, gid


def primary(config, tag):
    outbounds = config.get("outbounds", [])
    matches = [item for item in outbounds if item.get("tag") == tag] if tag else outbounds[:1]
    if len(matches) != 1 or matches[0].get("protocol") not in (
            "vmess", "vless", "trojan", "shadowsocks"):
        raise RuntimeError("Select exactly one existing remote proxy outbound with --outbound-tag.")
    stream = matches[0].get("streamSettings", {})
    if stream.get("sockopt", {}).get("dialerProxy"):
        raise RuntimeError("dialerProxy is configured; this V2Ray 4.x helper cannot change it.")
    return matches[0]


def candidate(config, mode, tag):
    result = copy.deepcopy(config)
    target = primary(result, tag)
    if mode == "direct":
        target.pop("proxySettings", None)
        return result
    stream = target.get("streamSettings", {})
    if stream.get("network", "tcp") != "tcp" or stream.get("security", "none") != "none":
        raise RuntimeError("Relay mode supports raw TCP only; retain transport settings on newer cores separately.")
    relay = {"tag": RELAY_TAG, "protocol": "http", "settings": {
        "servers": [{"address": "127.0.0.1", "port": 11111}]}}
    existing = [item for item in result["outbounds"] if item.get("tag") == RELAY_TAG]
    if existing and (len(existing) != 1 or existing[0] != relay):
        raise RuntimeError("Existing relay tag has different settings; refusing to overwrite it.")
    if not existing:
        result["outbounds"].append(relay)
    target["proxySettings"] = {"tag": RELAY_TAG}
    return result


def check_http(port, url, expected):
    result = command(["curl", "--silent", "--show-error", "--noproxy", "",
                      "--proxy", f"http://127.0.0.1:{port}", "--connect-timeout", "10",
                      "--max-time", "20", "--output", "/dev/null", "--write-out",
                      "%{http_code} %{time_total}", url], timeout=24)
    code = result.stdout.split()[0] if result.stdout.split() else "000"
    ok = result.returncode == 0 and code == expected
    print(f"{'OK' if ok else 'FAIL'} {url}: {result.stdout.strip()} curl={result.returncode}", flush=True)
    return ok


def check_proxy(port):
    # Two rounds avoid accepting a single successful request on a flaky route.
    results = []
    for _ in range(2):
        with concurrent.futures.ThreadPoolExecutor(max_workers=len(CHECKS)) as pool:
            futures = [pool.submit(check_http, port, url, status) for url, status in CHECKS]
            results.extend(job.result() for job in futures)
    return all(results)


def validate(binary, path):
    # Matches the existing V2Ray 4.x systemd ExecStart, without upgrading it.
    result = command([binary, "-test", "-config", str(path)])
    if result.returncode:
        raise RuntimeError("V2Ray configuration validation failed (private config output suppressed).")


def probe(config, args, uid, gid, directory):
    probe_config = copy.deepcopy(config)
    target = primary(probe_config, args.outbound_tag)
    with socket.socket() as listener:
        listener.bind(("127.0.0.1", 0))
        port = listener.getsockname()[1]
    # Make the selected remote outbound the default and force probe requests to it;
    # production split-routing rules must not create a false successful direct test.
    probe_config["outbounds"].remove(target)
    probe_config["outbounds"].insert(0, target)
    probe_config["inbounds"] = [{"listen": "127.0.0.1", "port": port,
                                  "protocol": "http", "settings": {}}]
    probe_config["routing"] = {"domainStrategy": "AsIs", "rules": []}
    probe_config["log"] = {"loglevel": "warning"}
    for key in ("api", "reverse", "observatory", "burstObservatory"):
        probe_config.pop(key, None)
    path = directory / "probe.json"
    path.write_text(json.dumps(probe_config), encoding="utf-8")
    os.chown(path, 0, gid)
    path.chmod(0o640)
    validate(args.binary, path)
    with (directory / "probe.log").open("wb") as log:
        process = subprocess.Popen([args.binary, "-config", str(path)],
                                   stdin=subprocess.DEVNULL, stdout=log, stderr=log,
                                   cwd="/", user=uid, group=gid, extra_groups=[])
        try:
            deadline = time.monotonic() + 5
            while time.monotonic() < deadline:
                if process.poll() is not None:
                    raise RuntimeError("Isolated V2Ray probe exited before becoming ready.")
                try:
                    with socket.create_connection(("127.0.0.1", port), timeout=0.2):
                        break
                except OSError:
                    time.sleep(0.1)
            else:
                raise RuntimeError("Isolated V2Ray probe listener did not become ready.")
            return check_proxy(port) and process.poll() is None
        finally:
            if process.poll() is None:
                process.terminate()
                try:
                    process.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    process.kill()
                    process.wait(timeout=5)


def atomic_write(path, data, metadata):
    fd, temporary = tempfile.mkstemp(prefix=".v2ray-route-", suffix=".json", dir=path.parent)
    try:
        with os.fdopen(fd, "wb") as output:
            output.write(data)
            output.flush()
            os.fsync(output.fileno())
            os.fchown(output.fileno(), metadata.st_uid, metadata.st_gid)
            os.fchmod(output.fileno(), stat.S_IMODE(metadata.st_mode))
        os.replace(temporary, path)
    finally:
        if os.path.exists(temporary):
            os.unlink(temporary)


def restart(service):
    if command(["systemctl", "restart", service]).returncode:
        raise RuntimeError("Service restart failed.")
    if command(["systemctl", "is-active", "--quiet", service]).returncode:
        raise RuntimeError("Service is not active after restart.")


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("mode", choices=("status", "test-direct", "direct", "relay"))
    parser.add_argument("--config", type=Path, default=Path("/usr/local/etc/v2ray/new.json"))
    parser.add_argument("--service", default="v2ray@new.service")
    parser.add_argument("--binary", default="/usr/local/bin/v2ray")
    parser.add_argument("--outbound-tag", default=None)
    parser.add_argument("--backup-dir", type=Path, default=Path("/var/backups/v2ray-route"))
    args = parser.parse_args()
    if os.geteuid() != 0:
        parser.error("Run with sudo inside the VM.")
    args.config = args.config.resolve(strict=True)
    with open("/run/lock/v2ray-11119-route.lock", "a") as lock:
        fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
        raw = args.config.read_bytes()
        metadata = args.config.stat()
        config = json.loads(raw)
        current = primary(config, args.outbound_tag).get("proxySettings", {}).get("tag")
        route = "direct" if not current else ("relay via 127.0.0.1:11111" if current == RELAY_TAG else "other relay")
        print(f"Current route: {route}", flush=True)
        if args.mode == "status":
            print("Service: " + command(["systemctl", "is-active", args.service]).stdout.strip())
            return
        if not any(item.get("protocol") == "http" and item.get("port") == 11119
                   for item in config.get("inbounds", [])):
            raise RuntimeError("Expected HTTP inbound on 11119 is absent.")
        if command(["systemctl", "is-active", "--quiet", args.service]).returncode:
            raise RuntimeError("Start the existing service before testing or switching routes.")
        mode = "direct" if args.mode == "test-direct" else args.mode
        changed = candidate(config, mode, args.outbound_tag)
        uid, gid = service_account(args.service)
        with tempfile.TemporaryDirectory(prefix="v2ray-route-", dir="/run") as temp:
            directory = Path(temp)
            os.chown(directory, 0, gid)
            directory.chmod(0o710)
            print(f"Testing {mode} with an isolated listener as service uid={uid}...", flush=True)
            if not probe(changed, args, uid, gid, directory):
                raise RuntimeError("Candidate route failed; production config and service were left unchanged.")
            if args.mode == "test-direct":
                print("Direct route passed. Use 'direct' to switch production.")
                return
            if config == changed:
                print("Already using the requested route; no restart needed.")
                return
            candidate_path = directory / "candidate.json"
            encoded = (json.dumps(changed, ensure_ascii=False, indent=2) + "\n").encode()
            candidate_path.write_bytes(encoded)
            candidate_path.chmod(0o600)
            validate(args.binary, candidate_path)
            if args.config.read_bytes() != raw:
                raise RuntimeError("Config changed during the probe; rerun against the new version.")
            backup_dir = args.backup_dir
            backup_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
            backup_dir.chmod(0o700)
            stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S-%f")
            backup = backup_dir / f"{args.config.name}.{stamp}.bak"
            fd = os.open(backup, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
            with os.fdopen(fd, "wb") as output:
                output.write(raw)
                output.flush()
                os.fsync(output.fileno())
            print(f"Private backup: {backup}", flush=True)
            atomic_write(args.config, encoded, metadata)
            try:
                restart(args.service)
                if not check_proxy(11119):
                    raise RuntimeError("Production HTTP checks failed.")
            except BaseException as problem:
                atomic_write(args.config, raw, metadata)
                restart(args.service)
                restored = check_proxy(11119)
                raise RuntimeError(f"Previous config restored; previous route healthy={restored}.") from problem
            print(f"Switched to {mode}; production HTTPS checks passed.")


if __name__ == "__main__":
    try:
        main()
    except (RuntimeError, OSError, ValueError, subprocess.SubprocessError) as error:
        # Config parsing and process errors may contain private values: do not dump
        # the config, V2Ray log, subprocess output, or Python exception traceback.
        message = str(error) if isinstance(error, RuntimeError) else type(error).__name__
        raise SystemExit("ERROR: " + message)

附录

v2ray 完整安装脚本

sh
#!/usr/bin/env bash
# shellcheck disable=SC2268

# The files installed by the script conform to the Filesystem Hierarchy Standard:
# https://wiki.linuxfoundation.org/lsb/fhs

# The URL of the script project is:
# https://github.com/v2fly/fhs-install-v2ray

# The URL of the script is:
# https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh

# If the script executes incorrectly, go to:
# https://github.com/v2fly/fhs-install-v2ray/issues

# You can set this variable whatever you want in shell session right before running this script by issuing:
# export DAT_PATH='/usr/local/share/v2ray'
DAT_PATH=${DAT_PATH:-/usr/local/share/v2ray}

# You can set this variable whatever you want in shell session right before running this script by issuing:
# export JSON_PATH='/usr/local/etc/v2ray'
JSON_PATH=${JSON_PATH:-/usr/local/etc/v2ray}

# Set this variable only if you are starting v2ray with multiple configuration files:
# export JSONS_PATH='/usr/local/etc/v2ray'

# Set this variable only if you want this script to check all the systemd unit file:
# export check_all_service_files='yes'

curl() {
  $(type -P curl) -L -q --retry 5 --retry-delay 10 --retry-max-time 60 "$@"
}

systemd_cat_config() {
  if systemd-analyze --help | grep -qw 'cat-config'; then
    systemd-analyze --no-pager cat-config "$@"
    echo
  else
    echo "${aoi}~~~~~~~~~~~~~~~~"
    cat "$@" "$1".d/*
    echo "${aoi}~~~~~~~~~~~~~~~~"
    echo "${red}warning: ${green}The systemd version on the current operating system is too low."
    echo "${red}warning: ${green}Please consider to upgrade the systemd or the operating system.${reset}"
    echo
  fi
}

check_if_running_as_root() {
  # If you want to run as another user, please modify $UID to be owned by this user
  if [[ "$UID" -ne '0' ]]; then
    echo "WARNING: The user currently executing this script is not root. You may encounter the insufficient privilege error."
    read -r -p "Are you sure you want to continue? [y/n] " cont_without_been_root
    if [[ x"${cont_without_been_root:0:1}" = x'y' ]]; then
      echo "Continuing the installation with current user..."
    else
      echo "Not running with root, exiting..."
      exit 1
    fi
  fi
}

identify_the_operating_system_and_architecture() {
  if [[ "$(uname)" == 'Linux' ]]; then
    case "$(uname -m)" in
      'i386' | 'i686')
        MACHINE='32'
        ;;
      'amd64' | 'x86_64')
        MACHINE='64'
        ;;
      'armv5tel')
        MACHINE='arm32-v5'
        ;;
      'armv6l')
        MACHINE='arm32-v6'
        grep Features /proc/cpuinfo | grep -qw 'vfp' || MACHINE='arm32-v5'
        ;;
      'armv7' | 'armv7l')
        MACHINE='arm32-v7a'
        grep Features /proc/cpuinfo | grep -qw 'vfp' || MACHINE='arm32-v5'
        ;;
      'armv8' | 'aarch64')
        MACHINE='arm64-v8a'
        ;;
      'mips')
        MACHINE='mips32'
        ;;
      'mipsle')
        MACHINE='mips32le'
        ;;
      'mips64')
        MACHINE='mips64'
        ;;
      'mips64le')
        MACHINE='mips64le'
        ;;
      'ppc64')
        MACHINE='ppc64'
        ;;
      'ppc64le')
        MACHINE='ppc64le'
        ;;
      'riscv64')
        MACHINE='riscv64'
        ;;
      's390x')
        MACHINE='s390x'
        ;;
      *)
        echo "error: The architecture is not supported."
        exit 1
        ;;
    esac
    if [[ ! -f '/etc/os-release' ]]; then
      echo "error: Don't use outdated Linux distributions."
      exit 1
    fi
    # Do not combine this judgment condition with the following judgment condition.
    ## Be aware of Linux distribution like Gentoo, which kernel supports switch between Systemd and OpenRC.
    ### Refer: https://github.com/v2fly/fhs-install-v2ray/issues/84#issuecomment-688574989
    if [[ -f /.dockerenv ]] || grep -q 'docker\|lxc' /proc/1/cgroup && [[ "$(type -P systemctl)" ]]; then
      true
    elif [[ -d /run/systemd/system ]] || grep -q systemd <(ls -l /sbin/init); then
      true
    else
      echo "error: Only Linux distributions using systemd are supported."
      exit 1
    fi
    if [[ "$(type -P apt)" ]]; then
      PACKAGE_MANAGEMENT_INSTALL='apt -y --no-install-recommends install'
      PACKAGE_MANAGEMENT_REMOVE='apt purge'
      package_provide_tput='ncurses-bin'
    elif [[ "$(type -P dnf)" ]]; then
      PACKAGE_MANAGEMENT_INSTALL='dnf -y install'
      PACKAGE_MANAGEMENT_REMOVE='dnf remove'
      package_provide_tput='ncurses'
    elif [[ "$(type -P yum)" ]]; then
      PACKAGE_MANAGEMENT_INSTALL='yum -y install'
      PACKAGE_MANAGEMENT_REMOVE='yum remove'
      package_provide_tput='ncurses'
    elif [[ "$(type -P zypper)" ]]; then
      PACKAGE_MANAGEMENT_INSTALL='zypper install -y --no-recommends'
      PACKAGE_MANAGEMENT_REMOVE='zypper remove'
      package_provide_tput='ncurses-utils'
    elif [[ "$(type -P pacman)" ]]; then
      PACKAGE_MANAGEMENT_INSTALL='pacman -Syu --noconfirm'
      PACKAGE_MANAGEMENT_REMOVE='pacman -Rsn'
      package_provide_tput='ncurses'
    else
      echo "error: The script does not support the package manager in this operating system."
      exit 1
    fi
  else
    echo "error: This operating system is not supported."
    exit 1
  fi
}

## Demo function for processing parameters
judgment_parameters() {
  while [[ "$#" -gt '0' ]]; do
    case "$1" in
      '--remove')
        if [[ "$#" -gt '1' ]]; then
          echo 'error: Please enter the correct parameters.'
          exit 1
        fi
        REMOVE='1'
        ;;
      '--version')
        VERSION="${2:?error: Please specify the correct version.}"
        break
        ;;
      '-c' | '--check')
        CHECK='1'
        break
        ;;
      '-f' | '--force')
        FORCE='1'
        break
        ;;
      '-h' | '--help')
        HELP='1'
        break
        ;;
      '-l' | '--local')
        LOCAL_INSTALL='1'
        LOCAL_FILE="${2:?error: Please specify the correct local file.}"
        break
        ;;
      '-p' | '--proxy')
        if [[ -z "${2:?error: Please specify the proxy server address.}" ]]; then
          exit 1
        fi
        PROXY="$2"
        shift
        ;;
      *)
        echo "$0: unknown option -- -"
        exit 1
        ;;
    esac
    shift
  done
}

install_software() {
  package_name="$1"
  file_to_detect="$2"
  type -P "$file_to_detect" > /dev/null 2>&1 && return
  if ${PACKAGE_MANAGEMENT_INSTALL} "$package_name"; then
    echo "info: $package_name is installed."
  else
    echo "error: Installation of $package_name failed, please check your network."
    exit 1
  fi
}

get_current_version() {
  if /usr/local/bin/v2ray -version > /dev/null 2>&1; then
    VERSION="$(/usr/local/bin/v2ray -version | awk 'NR==1 {print $2}')"
  else
    VERSION="$(/usr/local/bin/v2ray version | awk 'NR==1 {print $2}')"
  fi
  CURRENT_VERSION="v${VERSION#v}"
}

get_version() {
  # 0: Install or update V2Ray.
  # 1: Installed or no new version of V2Ray.
  # 2: Install the specified version of V2Ray.
  if [[ -n "$VERSION" ]]; then
    RELEASE_VERSION="v${VERSION#v}"
    return 2
  fi
  # Determine the version number for V2Ray installed from a local file
  if [[ -f '/usr/local/bin/v2ray' ]]; then
    get_current_version
    if [[ "$LOCAL_INSTALL" -eq '1' ]]; then
      RELEASE_VERSION="$CURRENT_VERSION"
      return
    fi
  fi
#   # Get V2Ray release version number
#   TMP_FILE="$(mktemp)"
#   if ! curl -x "${PROXY}" -sS -i -H "Accept: application/vnd.github.v3+json" -o "$TMP_FILE" 'https://api.github.com/repos/v2fly/v2ray-core/releases/latest'; then
#     "rm" "$TMP_FILE"
#     echo 'error: Failed to get release list, please check your network.'
#     exit 1
#   fi
#   HTTP_STATUS_CODE=$(awk 'NR==1 {print $2}' "$TMP_FILE")
#   if [[ $HTTP_STATUS_CODE -lt 200 ]] || [[ $HTTP_STATUS_CODE -gt 299 ]]; then
#     "rm" "$TMP_FILE"
#     echo "error: Failed to get release list, GitHub API response code: $HTTP_STATUS_CODE"
#     exit 1
#   fi
#   RELEASE_LATEST="$(sed 'y/,/\n/' "$TMP_FILE" | grep 'tag_name' | awk -F '"' '{print $4}')"
#   "rm" "$TMP_FILE"
#   RELEASE_VERSION="v${RELEASE_LATEST#v}"
    RELEASE_VERSION="v4.28.2"
  # Compare V2Ray version numbers
  if [[ "$RELEASE_VERSION" != "$CURRENT_VERSION" ]]; then
    RELEASE_VERSIONSION_NUMBER="${RELEASE_VERSION#v}"
    RELEASE_MAJOR_VERSION_NUMBER="${RELEASE_VERSIONSION_NUMBER%%.*}"
    RELEASE_MINOR_VERSION_NUMBER="$(echo "$RELEASE_VERSIONSION_NUMBER" | awk -F '.' '{print $2}')"
    RELEASE_MINIMUM_VERSION_NUMBER="${RELEASE_VERSIONSION_NUMBER##*.}"
    # shellcheck disable=SC2001
    CURRENT_VERSION_NUMBER="$(echo "${CURRENT_VERSION#v}" | sed 's/-.*//')"
    CURRENT_MAJOR_VERSION_NUMBER="${CURRENT_VERSION_NUMBER%%.*}"
    CURRENT_MINOR_VERSION_NUMBER="$(echo "$CURRENT_VERSION_NUMBER" | awk -F '.' '{print $2}')"
    CURRENT_MINIMUM_VERSION_NUMBER="${CURRENT_VERSION_NUMBER##*.}"
    if [[ "$RELEASE_MAJOR_VERSION_NUMBER" -gt "$CURRENT_MAJOR_VERSION_NUMBER" ]]; then
      return 0
    elif [[ "$RELEASE_MAJOR_VERSION_NUMBER" -eq "$CURRENT_MAJOR_VERSION_NUMBER" ]]; then
      if [[ "$RELEASE_MINOR_VERSION_NUMBER" -gt "$CURRENT_MINOR_VERSION_NUMBER" ]]; then
        return 0
      elif [[ "$RELEASE_MINOR_VERSION_NUMBER" -eq "$CURRENT_MINOR_VERSION_NUMBER" ]]; then
        if [[ "$RELEASE_MINIMUM_VERSION_NUMBER" -gt "$CURRENT_MINIMUM_VERSION_NUMBER" ]]; then
          return 0
        else
          return 1
        fi
      else
        return 1
      fi
    else
      return 1
    fi
  elif [[ "$RELEASE_VERSION" == "$CURRENT_VERSION" ]]; then
    return 1
  fi
}

download_v2ray() {
  DOWNLOAD_LINK="https://githubfast.com/v2fly/v2ray-core/releases/download/$RELEASE_VERSION/v2ray-linux-$MACHINE.zip"
  echo "Downloading V2Ray archive: $DOWNLOAD_LINK"
  if ! curl -x "${PROXY}" -R -H 'Cache-Control: no-cache' -o "$ZIP_FILE" "$DOWNLOAD_LINK"; then
    echo 'error: Download failed! Please check your network or try again.'
    return 1
  fi
#   echo "Downloading verification file for V2Ray archive: $DOWNLOAD_LINK.dgst"
#   if ! curl -x "${PROXY}" -sSR -H 'Cache-Control: no-cache' -o "$ZIP_FILE.dgst" "$DOWNLOAD_LINK.dgst"; then
#     echo 'error: Download failed! Please check your network or try again.'
#     return 1
#   fi
#   if [[ "$(cat "$ZIP_FILE".dgst)" == 'Not Found' ]]; then
#     echo 'error: This version does not support verification. Please replace with another version.'
#     return 1
#   fi

#   # Verification of V2Ray archive
#   CHECKSUM=$(awk -F '= ' '/256=/ {print $2}' < "${ZIP_FILE}.dgst")
#   LOCALSUM=$(sha256sum "$ZIP_FILE" | awk '{printf $1}')
#   if [[ "$CHECKSUM" != "$LOCALSUM" ]]; then
#     echo 'error: SHA256 check failed! Please check your network or try again.'
#     return 1
#   fi
}

decompression() {
  if ! unzip -q "$1" -d "$TMP_DIRECTORY"; then
    echo 'error: V2Ray decompression failed.'
    "rm" -r "$TMP_DIRECTORY"
    echo "removed: $TMP_DIRECTORY"
    exit 1
  fi
  echo "info: Extract the V2Ray package to $TMP_DIRECTORY and prepare it for installation."
}

install_file() {
  NAME="$1"
  if [[ "$NAME" == 'v2ray' ]] || [[ "$NAME" == 'v2ctl' ]]; then
    install -m 755 "${TMP_DIRECTORY}/$NAME" "/usr/local/bin/$NAME"
  elif [[ "$NAME" == 'geoip.dat' ]] || [[ "$NAME" == 'geosite.dat' ]]; then
    install -m 644 "${TMP_DIRECTORY}/$NAME" "${DAT_PATH}/$NAME"
  fi
}

install_v2ray() {
  # Install V2Ray binary to /usr/local/bin/ and $DAT_PATH
  install_file v2ray
  if [[ -f "${TMP_DIRECTORY}/v2ctl" ]]; then
    install_file v2ctl
  else
    if [[ -f '/usr/local/bin/v2ctl' ]]; then
      rm '/usr/local/bin/v2ctl'
    fi
  fi
  install -d "$DAT_PATH"
  # If the file exists, geoip.dat and geosite.dat will not be installed or updated
  if [[ ! -f "${DAT_PATH}/.undat" ]]; then
    install_file geoip.dat
    install_file geosite.dat
  fi

  # Install V2Ray configuration file to $JSON_PATH
  # shellcheck disable=SC2153
  if [[ -z "$JSONS_PATH" ]] && [[ ! -d "$JSON_PATH" ]]; then
    install -d "$JSON_PATH"
    echo "{}" > "${JSON_PATH}/config.json"
    CONFIG_NEW='1'
  fi

  # Install V2Ray configuration file to $JSONS_PATH
  if [[ -n "$JSONS_PATH" ]] && [[ ! -d "$JSONS_PATH" ]]; then
    install -d "$JSONS_PATH"
    for BASE in 00_log 01_api 02_dns 03_routing 04_policy 05_inbounds 06_outbounds 07_transport 08_stats 09_reverse; do
      echo '{}' > "${JSONS_PATH}/${BASE}.json"
    done
    CONFDIR='1'
  fi

  # Used to store V2Ray log files
  if [[ ! -d '/var/log/v2ray/' ]]; then
    if id nobody | grep -qw 'nogroup'; then
      install -d -m 700 -o nobody -g nogroup /var/log/v2ray/
      install -m 600 -o nobody -g nogroup /dev/null /var/log/v2ray/access.log
      install -m 600 -o nobody -g nogroup /dev/null /var/log/v2ray/error.log
    else
      install -d -m 700 -o nobody -g nobody /var/log/v2ray/
      install -m 600 -o nobody -g nobody /dev/null /var/log/v2ray/access.log
      install -m 600 -o nobody -g nobody /dev/null /var/log/v2ray/error.log
    fi
    LOG='1'
  fi
}

install_startup_service_file() {
  get_current_version
  if [[ "$(echo "${CURRENT_VERSION#v}" | sed 's/-.*//' | awk -F'.' '{print $1}')" -gt "4" ]]; then
    START_COMMAND="/usr/local/bin/v2ray run"
  else
    START_COMMAND="/usr/local/bin/v2ray"
  fi
  install -m 644 "${TMP_DIRECTORY}/systemd/system/v2ray.service" /etc/systemd/system/v2ray.service
  install -m 644 "${TMP_DIRECTORY}/systemd/system/v2ray@.service" /etc/systemd/system/v2ray@.service
  mkdir -p '/etc/systemd/system/v2ray.service.d'
  mkdir -p '/etc/systemd/system/v2ray@.service.d/'
  if [[ -n "$JSONS_PATH" ]]; then
    "rm" -f '/etc/systemd/system/v2ray.service.d/10-donot_touch_single_conf.conf' \
      '/etc/systemd/system/v2ray@.service.d/10-donot_touch_single_conf.conf'
    echo "# In case you have a good reason to do so, duplicate this file in the same directory and make your customizes there.
# Or all changes you made will be lost!  # Refer: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
[Service]
ExecStart=
ExecStart=${START_COMMAND} -confdir $JSONS_PATH" |
      tee '/etc/systemd/system/v2ray.service.d/10-donot_touch_multi_conf.conf' > '/etc/systemd/system/v2ray@.service.d/10-donot_touch_multi_conf.conf'
  else
    "rm" -f '/etc/systemd/system/v2ray.service.d/10-donot_touch_multi_conf.conf' \
      '/etc/systemd/system/v2ray@.service.d/10-donot_touch_multi_conf.conf'
    echo "# In case you have a good reason to do so, duplicate this file in the same directory and make your customizes there.
# Or all changes you made will be lost!  # Refer: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
[Service]
ExecStart=
ExecStart=${START_COMMAND} -config ${JSON_PATH}/config.json" > '/etc/systemd/system/v2ray.service.d/10-donot_touch_single_conf.conf'
    echo "# In case you have a good reason to do so, duplicate this file in the same directory and make your customizes there.
# Or all changes you made will be lost!  # Refer: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
[Service]
ExecStart=
ExecStart=${START_COMMAND} -config ${JSON_PATH}/%i.json" > '/etc/systemd/system/v2ray@.service.d/10-donot_touch_single_conf.conf'
  fi
  echo "info: Systemd service files have been installed successfully!"
  echo "${red}warning: ${green}The following are the actual parameters for the v2ray service startup."
  echo "${red}warning: ${green}Please make sure the configuration file path is correctly set.${reset}"
  systemd_cat_config /etc/systemd/system/v2ray.service
  # shellcheck disable=SC2154
  if [[ x"${check_all_service_files:0:1}" = x'y' ]]; then
    echo
    echo
    systemd_cat_config /etc/systemd/system/v2ray@.service
  fi
  systemctl daemon-reload
  SYSTEMD='1'
}

start_v2ray() {
  if [[ -f '/etc/systemd/system/v2ray.service' ]]; then
    if systemctl start "${V2RAY_CUSTOMIZE:-v2ray}"; then
      echo 'info: Start the V2Ray service.'
    else
      echo 'error: Failed to start V2Ray service.'
      exit 1
    fi
  fi
}

stop_v2ray() {
  V2RAY_CUSTOMIZE="$(systemctl list-units | grep 'v2ray@' | awk -F ' ' '{print $1}')"
  if [[ -z "$V2RAY_CUSTOMIZE" ]]; then
    local v2ray_daemon_to_stop='v2ray.service'
  else
    local v2ray_daemon_to_stop="$V2RAY_CUSTOMIZE"
  fi
  if ! systemctl stop "$v2ray_daemon_to_stop"; then
    echo 'error: Stopping the V2Ray service failed.'
    exit 1
  fi
  echo 'info: Stop the V2Ray service.'
}

check_update() {
  if [[ -f '/etc/systemd/system/v2ray.service' ]]; then
    get_version
    local get_ver_exit_code=$?
    if [[ "$get_ver_exit_code" -eq '0' ]]; then
      echo "info: Found the latest release of V2Ray $RELEASE_VERSION . (Current release: $CURRENT_VERSION)"
    elif [[ "$get_ver_exit_code" -eq '1' ]]; then
      echo "info: No new version. The current version of V2Ray is $CURRENT_VERSION ."
    fi
    exit 0
  else
    echo 'error: V2Ray is not installed.'
    exit 1
  fi
}

remove_v2ray() {
  if systemctl list-unit-files | grep -qw 'v2ray'; then
    if [[ -n "$(pidof v2ray)" ]]; then
      stop_v2ray
    fi
    if ! ("rm" -r '/usr/local/bin/v2ray' \
      "$DAT_PATH" \
      '/etc/systemd/system/v2ray.service' \
      '/etc/systemd/system/v2ray@.service' \
      '/etc/systemd/system/v2ray.service.d' \
      '/etc/systemd/system/v2ray@.service.d'); then
      echo 'error: Failed to remove V2Ray.'
      exit 1
    else
      echo 'removed: /usr/local/bin/v2ray'
      if [[ -f '/usr/local/bin/v2ctl' ]]; then
        rm '/usr/local/bin/v2ctl'
        echo 'removed: /usr/local/bin/v2ctl'
      fi
      echo "removed: $DAT_PATH"
      echo 'removed: /etc/systemd/system/v2ray.service'
      echo 'removed: /etc/systemd/system/v2ray@.service'
      echo 'removed: /etc/systemd/system/v2ray.service.d'
      echo 'removed: /etc/systemd/system/v2ray@.service.d'
      echo 'Please execute the command: systemctl disable v2ray'
      echo "You may need to execute a command to remove dependent software: $PACKAGE_MANAGEMENT_REMOVE curl unzip"
      echo 'info: V2Ray has been removed.'
      echo 'info: If necessary, manually delete the configuration and log files.'
      if [[ -n "$JSONS_PATH" ]]; then
        echo "info: e.g., $JSONS_PATH and /var/log/v2ray/ ..."
      else
        echo "info: e.g., $JSON_PATH and /var/log/v2ray/ ..."
      fi
      exit 0
    fi
  else
    echo 'error: V2Ray is not installed.'
    exit 1
  fi
}

# Explanation of parameters in the script
show_help() {
  echo "usage: $0 [--remove | --version number | -c | -f | -h | -l | -p]"
  echo '  [-p address] [--version number | -c | -f]'
  echo '  --remove        Remove V2Ray'
  echo '  --version       Install the specified version of V2Ray, e.g., --version v4.18.0'
  echo '  -c, --check     Check if V2Ray can be updated'
  echo '  -f, --force     Force installation of the latest version of V2Ray'
  echo '  -h, --help      Show help'
  echo '  -l, --local     Install V2Ray from a local file'
  echo '  -p, --proxy     Download through a proxy server, e.g., -p http://127.0.0.1:8118 or -p socks5://127.0.0.1:1080'
  exit 0
}

main() {
  check_if_running_as_root
  identify_the_operating_system_and_architecture
  judgment_parameters "$@"

  install_software "$package_provide_tput" 'tput'
  red=$(tput setaf 1)
  green=$(tput setaf 2)
  aoi=$(tput setaf 6)
  reset=$(tput sgr0)

  # Parameter information
  [[ "$HELP" -eq '1' ]] && show_help
  [[ "$CHECK" -eq '1' ]] && check_update
  [[ "$REMOVE" -eq '1' ]] && remove_v2ray

  # Two very important variables
  TMP_DIRECTORY="$(mktemp -d)"
  ZIP_FILE="${TMP_DIRECTORY}/v2ray-linux-$MACHINE.zip"

  # Install V2Ray from a local file, but still need to make sure the network is available
  if [[ "$LOCAL_INSTALL" -eq '1' ]]; then
    echo 'warn: Install V2Ray from a local file, but still need to make sure the network is available.'
    echo -n 'warn: Please make sure the file is valid because we cannot confirm it. (Press any key) ...'
    read -r
    install_software 'unzip' 'unzip'
    decompression "$LOCAL_FILE"
  else
    # Normal way
    install_software 'curl' 'curl'
    get_version
    NUMBER="$?"
    if [[ "$NUMBER" -eq '0' ]] || [[ "$FORCE" -eq '1' ]] || [[ "$NUMBER" -eq 2 ]]; then
      echo "info: Installing V2Ray $RELEASE_VERSION for $(uname -m)"
      download_v2ray
      if [[ "$?" -eq '1' ]]; then
        "rm" -r "$TMP_DIRECTORY"
        echo "removed: $TMP_DIRECTORY"
        exit 1
      fi
      install_software 'unzip' 'unzip'
      decompression "$ZIP_FILE"
    elif [[ "$NUMBER" -eq '1' ]]; then
      echo "info: No new version. The current version of V2Ray is $CURRENT_VERSION ."
      exit 0
    fi
  fi

  # Determine if V2Ray is running
  if systemctl list-unit-files | grep -qw 'v2ray'; then
    if [[ -n "$(pidof v2ray)" ]]; then
      stop_v2ray
      V2RAY_RUNNING='1'
    fi
  fi
  install_v2ray
  install_startup_service_file
  echo 'installed: /usr/local/bin/v2ray'
  if [[ -f '/usr/local/bin/v2ctl' ]]; then
    echo 'installed: /usr/local/bin/v2ctl'
  fi
  # If the file exists, the content output of installing or updating geoip.dat and geosite.dat will not be displayed
  if [[ ! -f "${DAT_PATH}/.undat" ]]; then
    echo "installed: ${DAT_PATH}/geoip.dat"
    echo "installed: ${DAT_PATH}/geosite.dat"
  fi
  if [[ "$CONFIG_NEW" -eq '1' ]]; then
    echo "installed: ${JSON_PATH}/config.json"
  fi
  if [[ "$CONFDIR" -eq '1' ]]; then
    echo "installed: ${JSON_PATH}/00_log.json"
    echo "installed: ${JSON_PATH}/01_api.json"
    echo "installed: ${JSON_PATH}/02_dns.json"
    echo "installed: ${JSON_PATH}/03_routing.json"
    echo "installed: ${JSON_PATH}/04_policy.json"
    echo "installed: ${JSON_PATH}/05_inbounds.json"
    echo "installed: ${JSON_PATH}/06_outbounds.json"
    echo "installed: ${JSON_PATH}/07_transport.json"
    echo "installed: ${JSON_PATH}/08_stats.json"
    echo "installed: ${JSON_PATH}/09_reverse.json"
  fi
  if [[ "$LOG" -eq '1' ]]; then
    echo 'installed: /var/log/v2ray/'
    echo 'installed: /var/log/v2ray/access.log'
    echo 'installed: /var/log/v2ray/error.log'
  fi
  if [[ "$SYSTEMD" -eq '1' ]]; then
    echo 'installed: /etc/systemd/system/v2ray.service'
    echo 'installed: /etc/systemd/system/v2ray@.service'
  fi
  "rm" -r "$TMP_DIRECTORY"
  echo "removed: $TMP_DIRECTORY"
  if [[ "$LOCAL_INSTALL" -eq '1' ]]; then
    get_version
  fi
  echo "info: V2Ray $RELEASE_VERSION is installed."
  echo "You may need to execute a command to remove dependent software: $PACKAGE_MANAGEMENT_REMOVE curl unzip"
  if [[ "$V2RAY_RUNNING" -eq '1' ]]; then
    start_v2ray
  else
    echo 'Please execute the command: systemctl enable v2ray; systemctl start v2ray'
  fi
}

main "$@"

config.json 完整样例

json
{
    "log": {
        "loglevel": "warning"
    },
    "inbounds": [
        {
            "port": 11110,
            "listen": "127.0.0.1",
            "protocol": "socks",
            "settings": {
                "auth": "noauth",
                "udp": true
            },
            "sniffing": {
                "enabled": true,
                "destOverride": ["http", "tls"]
            }
        },
        {
            "port": 11111,
            "listen": "127.0.0.1",
            "protocol": "http",
            "settings": {
                "auth": "noauth",
                "udp": false
            },
            "sniffing": {
                "enabled": true,
                "destOverride": ["http", "tls"]
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "vmess",
            "settings": {
                "vnext": [
                    {
                        "address": "***.***.***.***",
                        "port": 9999,
                        "users": [
                            {
                                "id": "********-****-****-****-************",
                                "alterId": 0
                            }
                        ]
                    }
                ]
            }
        },
        {
            "protocol": "freedom",
            "tag": "direct",
            "settings": {}
        }
    ],
    "routing": {
        "domainStrategy": "IPOnDemand",
        "rules": [
            {
                "type": "field",
                "ip": ["geoip:private"],
                "outboundTag": "direct"
            }
        ]
    }
}