🛰️ proxy —— Ubuntu 全局代理管理工具
一个 Shell 脚本,解决 Ubuntu 代理设置散乱、切换繁琐的痛点。
proxy on一条命令同时搞定 Shell、apt、git、Docker 四处代理,proxy off真正清除所有层级。
背景:为什么需要这个工具?
在 Ubuntu 上使用代理一直是一件「能用但麻烦」的事。你可能遇到过这些场景:
- 每次开机都要手动
export http_proxy=...,关掉终端就失效了 apt update走不了代理,要单独配置/etc/apt/apt.conf.d/git clone慢如蜗牛,git 有自己独立的代理设置docker pull超时,Docker daemon 根本不读 shell 环境变量
功能速览
| 命令 | 说明 |
|---|---|
proxy config 10.x.x.x:7890 | 配置代理地址(支持交互式向导) |
proxy on | 开启全局代理(Shell + RC + apt + git + Docker) |
proxy off | 关闭并清除所有层级代理(保留配置地址) |
proxy ping | 测试连通性(自动判断走代理还是直连) |
proxy ping <url> | 测试指定地址 |
proxy status | 查看五层代理状态 |
proxy -h | 帮助信息 |
安装
前置依赖
| 依赖 | 说明 |
|---|---|
bash | Ubuntu 默认已安装 |
curl | 用于 proxy ping 连通性测试(sudo apt install curl) |
git | 可选,proxy on 时自动配置 git 全局代理 |
docker | 可选,proxy on 时自动配置 Docker daemon 代理 |
sudo 权限 | 写入 apt / docker 配置及 systemd reload 时需要 |
一键安装
将 proxy 和 install_proxy.sh 两个文件放在同一目录,执行:
chmod +x proxy install_proxy.sh
sudo bash install_proxy.sh
安装脚本会自动完成:
- 检查
curl/bash依赖 - 将主脚本安装为
/usr/local/bin/_proxy_bin - 向
~/.bashrc(以及~/.zshrc,如果存在)注入proxyshell function - 引导完成首次代理地址配置(可回车跳过)
安装完成后执行一次让当前终端生效:
source ~/.bashrc
✅ 后续新开的终端无需再次 source,function 会随 bashrc 自动加载。
使用指南
第一步:配置代理地址
# 一次性设置 host 和 port(推荐)
proxy config 10.60.60.39:7890
# 单独修改 host 或 port
proxy config -host 10.60.60.39
proxy config -port 7890
# 交互式向导
proxy config
配置文件存储在 ~/.config/proxy-manager/config,权限自动设为 600。配置地址与代理开关状态相互独立——proxy off 不会删除配置,随时可以重新 proxy on。
第二步:开启代理
proxy on
一条命令同时配置五个层级:
| 层级 | 写入位置 | 生效范围 |
|---|---|---|
| Shell 环境变量 | 当前进程(eval) | 当前终端窗口,立即生效 |
| Shell RC 文件 | ~/.bashrc / ~/.zshrc | 新开终端自动加载 |
| apt 代理 | /etc/apt/apt.conf.d/95proxy | apt install / apt update |
| git 全局代理 | ~/.gitconfig | 所有 git 操作 |
| Docker daemon | /etc/systemd/system/docker.service.d/proxy.conf | docker pull / docker build |
执行后的输出示例:
✔ 代理已开启: http://10.60.60.39:7890
✔ Shell 环境变量 → 当前窗口立即生效
✔ ~/.bashrc → 新终端自动加载
✔ apt 代理 → /etc/apt/apt.conf.d/95proxy
✔ git 全局代理 → http.proxy / https.proxy
✔ docker 代理 → /etc/systemd/system/docker.service.d/proxy.conf
⚠️ Docker 代理生效需要重启 daemon,脚本会自动执行
systemctl restart docker。如果 Docker 当前未运行,配置写入后启动时自动生效。
关闭代理
proxy off
同步清除所有层级,保留配置地址:
✔ 代理已关闭
✔ Shell 环境变量 已清除(当前窗口)
✔ ~/.bashrc 已清除代理块
✔ apt 代理 已移除
✔ git 全局代理 已移除
✔ docker 代理 已移除
💡
proxy off是幂等的,重复执行不会报错。
测试连通性
proxy ping 根据当前窗口的代理状态自动决定测试方式:
proxy ping # 默认测试 https://www.google.com
proxy ping https://www.youtube.com # 测试指定地址
- 代理已开启:经由代理测试,验证代理是否正常工作
- 代理未开启:直连测试,验证目标地址是否可达
# 代理已开启时
ℹ 代理已开启,经由 http://10.60.60.39:7890 测试: https://www.google.com
✔ 连通性: 正常 HTTP 200 耗时 342ms
# 代理未开启时
ℹ 代理未开启,直连测试: https://www.google.com
✔ 连通性: 正常 HTTP 200 耗时 89ms
查看状态
proxy status
═══ 代理状态检测 ═══
配置地址 ✔ 10.60.60.39:7890 (~/.config/proxy-manager/config)
当前窗口 ✔ http://10.60.60.39:7890
Shell RC ✔ 代理已写入 ~/.bashrc(新终端自动生效)
apt 代理 ✔ /etc/apt/apt.conf.d/95proxy
git 代理 ✔ http://10.60.60.39:7890
docker ✔ /etc/systemd/system/docker.service.d/proxy.conf
设计说明
为什么各工具需要单独配置?
Linux 下不同工具有各自独立的代理机制,仅设置 shell 环境变量是不够的:
| 工具 | 代理机制 |
|---|---|
| curl / wget | 读取 http_proxy 环境变量 |
| apt | 只认 /etc/apt/apt.conf.d/ 配置文件 |
| git | 使用 git config http.proxy,独立于环境变量 |
| Docker daemon | 以 systemd service 运行,需通过 drop-in 文件注入环境变量给 dockerd 进程 |
proxy on 针对这四种机制分别写入,一条命令全部搞定。
Docker 代理的特殊性
Docker 有两个不同的代理配置场景,本工具解决的是 daemon 代理:
- daemon 代理(本工具配置):控制
dockerd进程拉取镜像时走代理,通过 systemd drop-in 文件/etc/systemd/system/docker.service.d/proxy.conf配置,需要重启 daemon 生效 - 容器代理:控制容器内部的网络请求,通过
~/.docker/config.json配置,不在本工具范围内
配置地址与开关状态分离
proxy off 仅清除代理的激活状态,不删除 ~/.config/proxy-manager/config 中保存的地址。临时关闭代理后,随时可以用 proxy on 重新启用,无需重新输入地址。
完整操作示例
# 1. 首次配置代理地址
proxy config 10.60.60.39:7890
# ✔ 代理已配置: 10.60.60.39:7890
# 2. 开启全局代理(含 Docker)
proxy on
# ✔ 代理已开启: http://10.60.60.39:7890
# ✔ 五层全部配置完毕,Docker daemon 已重启
# 3. 测试连通性(走代理)
proxy ping
# ✔ 连通性: 正常 HTTP 200 耗时 312ms
# 4. 拉取 Docker 镜像(走代理)
docker pull ubuntu:24.04
# 5. 查看当前状态
proxy status
# 五层全部显示 ✔
# 6. 临时关闭代理
proxy off
# ✔ 代理已关闭(五层全部清除,配置地址保留)
# 7. 修改代理地址后重新开启
proxy config -host 10.60.60.100
proxy on
获取脚本
完整源码包含两个文件:
proxy— 主脚本(安装为/usr/local/bin/_proxy_bin)install_proxy.sh— 安装脚本,需要sudo运行
💡 脚本逻辑简单透明,欢迎按需修改。
如果这个工具对你有帮助,欢迎分享给同样在 Ubuntu 上被代理折磨的朋友 🙂
proxy
#!/usr/bin/env bash
# =============================================================================
# _proxy_bin - proxy 工具真实执行脚本(通过 shell function 包装调用)
# 安装: sudo bash install_proxy.sh
# =============================================================================
#
# ── 设计说明 ──────────────────────────────────────────────────────────────────
# shell 子进程无法修改父进程的环境变量,因此 proxy on/off 对当前窗口的
# 环境变量操作必须在父 shell 中执行。
#
# 解决方案:
# 1. 本脚本(_proxy_bin)负责所有文件操作(rc/apt/git/docker)和用户输出
# 2. 对需要影响当前 shell 的操作,在 stdout 末尾输出 __EVAL__:<commands>
# 3. install_proxy.sh 在 ~/.bashrc 注入一个名为 proxy 的 shell function
# 4. 该 function 调用 _proxy_bin,捕获 __EVAL__ 行并在当前 shell 中 eval
# =============================================================================
set -euo pipefail
# ── 常量 ─────────────────────────────────────────────────────────────────────
CONFIG_FILE="$HOME/.config/proxy-manager/config"
RC_MARKER_BEGIN="# >>> proxy-manager global <<<"
RC_MARKER_END="# <<< proxy-manager global <<<"
BASHRC_FILE="$HOME/.bashrc"
ZSHRC_FILE="$HOME/.zshrc"
APT_PROXY_FILE="/etc/apt/apt.conf.d/95proxy"
DOCKER_PROXY_DIR="/etc/systemd/system/docker.service.d"
DOCKER_PROXY_FILE="${DOCKER_PROXY_DIR}/proxy.conf"
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'
# ── 工具函数 ──────────────────────────────────────────────────────────────────
info() { echo -e "${BLUE}ℹ${NC} $*"; }
success() { echo -e "${GREEN}✔${NC} $*"; }
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
error() { echo -e "${RED}✖${NC} $*" >&2; }
die() { error "$*"; exit 1; }
bold() { echo -e "${BOLD}$*${NC}"; }
# 向父 shell 发送需要 eval 的命令(由 proxy function 捕获执行)
emit_eval() { echo "__EVAL__:$*"; }
# ── 配置读写 ──────────────────────────────────────────────────────────────────
load_config() {
[[ -f "$CONFIG_FILE" ]] || return 1
# shellcheck source=/dev/null
source "$CONFIG_FILE"
}
save_config() {
local host="$1" port="$2"
mkdir -p "$(dirname "$CONFIG_FILE")"
cat > "$CONFIG_FILE" <<EOF
# proxy-manager 配置文件 — 请勿手动修改格式
PROXY_HOST="${host}"
PROXY_PORT="${port}"
EOF
chmod 600 "$CONFIG_FILE"
}
get_proxy_url() {
load_config || die "尚未配置代理,请先运行: proxy config <host>:<port>"
echo "http://${PROXY_HOST}:${PROXY_PORT}"
}
# ── 格式校验 ──────────────────────────────────────────────────────────────────
validate_host() {
local host="$1"
[[ "$host" =~ ^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]] \
|| [[ "$host" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] \
|| [[ "$host" == "localhost" ]]
}
validate_port() {
local port="$1"
[[ "$port" =~ ^[0-9]+$ ]] && (( port >= 1 && port <= 65535 ))
}
# ── 环境变量块 ────────────────────────────────────────────────────────────────
_export_cmds() {
local url="$1"
echo "export http_proxy='${url}' https_proxy='${url}' HTTP_PROXY='${url}' HTTPS_PROXY='${url}' ftp_proxy='${url}' FTP_PROXY='${url}' no_proxy='localhost,127.0.0.1,::1' NO_PROXY='localhost,127.0.0.1,::1'"
}
_unset_cmds() {
echo "unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ftp_proxy FTP_PROXY no_proxy NO_PROXY"
}
_rc_export_block() {
local url="$1"
cat <<EOF
export http_proxy="${url}"
export https_proxy="${url}"
export HTTP_PROXY="${url}"
export HTTPS_PROXY="${url}"
export ftp_proxy="${url}"
export FTP_PROXY="${url}"
export no_proxy="localhost,127.0.0.1,::1"
export NO_PROXY="localhost,127.0.0.1,::1"
EOF
}
# ── rc 文件操作 ───────────────────────────────────────────────────────────────
_write_rc() {
local url="$1" file="$2"
[[ -f "$file" ]] || return 0
sed -i "/# >>> proxy-manager global <<</,/# <<< proxy-manager global <<</d" "$file"
cat >> "$file" <<EOF
${RC_MARKER_BEGIN}
$(_rc_export_block "$url")
${RC_MARKER_END}
EOF
}
_remove_rc() {
local file="$1"
[[ -f "$file" ]] || return 0
sed -i "/# >>> proxy-manager global <<</,/# <<< proxy-manager global <<</d" "$file"
}
_rc_has_proxy() {
grep -q "proxy-manager global" "$BASHRC_FILE" 2>/dev/null
}
# ── apt 代理 ──────────────────────────────────────────────────────────────────
_write_apt() {
local host="$1" port="$2"
sudo tee "$APT_PROXY_FILE" > /dev/null <<EOF
# proxy-manager — apt 代理设置
Acquire::http::Proxy "http://${host}:${port}";
Acquire::https::Proxy "http://${host}:${port}";
EOF
sudo chmod 644 "$APT_PROXY_FILE"
}
_remove_apt() {
[[ -f "$APT_PROXY_FILE" ]] && sudo rm -f "$APT_PROXY_FILE" || true
}
_apt_has_proxy() { [[ -f "$APT_PROXY_FILE" ]]; }
# ── git 代理 ──────────────────────────────────────────────────────────────────
_write_git() {
local url="$1"
git config --global http.proxy "$url"
git config --global https.proxy "$url"
}
_remove_git() {
git config --global --unset http.proxy 2>/dev/null || true
git config --global --unset https.proxy 2>/dev/null || true
}
_git_has_proxy() {
command -v git &>/dev/null && git config --global --get http.proxy &>/dev/null
}
# ── docker 代理 ───────────────────────────────────────────────────────────────
# Docker daemon 拉取镜像走的是 dockerd 进程,不读取 shell 环境变量,
# 需要通过 systemd drop-in 文件配置 HTTP_PROXY 传给 dockerd,
# 并 reload systemd + restart docker 使其生效。
_write_docker() {
local url="$1"
if ! command -v docker &>/dev/null; then
warn "未检测到 docker,跳过 docker 代理配置"
return 0
fi
sudo mkdir -p "$DOCKER_PROXY_DIR"
sudo tee "$DOCKER_PROXY_FILE" > /dev/null <<EOF
# proxy-manager — docker daemon 代理设置
[Service]
Environment="HTTP_PROXY=${url}"
Environment="HTTPS_PROXY=${url}"
Environment="NO_PROXY=localhost,127.0.0.1"
EOF
sudo chmod 644 "$DOCKER_PROXY_FILE"
# reload systemd 配置并重启 docker daemon
sudo systemctl daemon-reload
if sudo systemctl is-active --quiet docker; then
sudo systemctl restart docker
success "Docker daemon 已重启,代理生效"
else
info "Docker daemon 当前未运行,代理配置已写入,启动时自动生效"
fi
}
_remove_docker() {
if [[ -f "$DOCKER_PROXY_FILE" ]]; then
sudo rm -f "$DOCKER_PROXY_FILE"
sudo systemctl daemon-reload
if sudo systemctl is-active --quiet docker 2>/dev/null; then
sudo systemctl restart docker
fi
fi
}
_docker_has_proxy() {
[[ -f "$DOCKER_PROXY_FILE" ]]
}
# ── 子命令:on ────────────────────────────────────────────────────────────────
cmd_on() {
local proxy_url
proxy_url="$(get_proxy_url)"
load_config || die "尚未配置代理"
# 1. rc 文件
_write_rc "$proxy_url" "$BASHRC_FILE"
[[ -f "$ZSHRC_FILE" ]] && _write_rc "$proxy_url" "$ZSHRC_FILE"
# 2. apt 代理
if sudo -n true 2>/dev/null; then
_write_apt "$PROXY_HOST" "$PROXY_PORT"
else
warn "配置系统代理需要 sudo 权限,请输入密码:"
_write_apt "$PROXY_HOST" "$PROXY_PORT"
fi
# 3. git 全局代理
if command -v git &>/dev/null; then
_write_git "$proxy_url"
else
warn "未检测到 git,跳过 git 代理配置"
fi
# 4. docker daemon 代理
_write_docker "$proxy_url"
# 5. 通知父 shell 导出环境变量
emit_eval "$(_export_cmds "$proxy_url")"
echo ""
success "代理已开启: ${CYAN}${proxy_url}${NC}"
echo ""
echo -e " ${GREEN}✔${NC} Shell 环境变量 → 当前窗口立即生效"
echo -e " ${GREEN}✔${NC} ~/.bashrc → 新终端自动加载"
[[ -f "$ZSHRC_FILE" ]] && \
echo -e " ${GREEN}✔${NC} ~/.zshrc → 新终端自动加载"
echo -e " ${GREEN}✔${NC} apt 代理 → ${APT_PROXY_FILE}"
command -v git &>/dev/null && \
echo -e " ${GREEN}✔${NC} git 全局代理 → http.proxy / https.proxy"
command -v docker &>/dev/null && \
echo -e " ${GREEN}✔${NC} docker 代理 → ${DOCKER_PROXY_FILE}"
echo ""
}
# ── 子命令:off ───────────────────────────────────────────────────────────────
cmd_off() {
# 1. rc 文件
_remove_rc "$BASHRC_FILE"
[[ -f "$ZSHRC_FILE" ]] && _remove_rc "$ZSHRC_FILE"
# 2. apt 代理
_remove_apt
# 3. git 代理
if command -v git &>/dev/null; then
_remove_git
fi
# 4. docker 代理
_remove_docker
# 5. 通知父 shell 清除环境变量
emit_eval "$(_unset_cmds)"
echo ""
success "代理已关闭"
echo ""
echo -e " ${GREEN}✔${NC} Shell 环境变量 已清除(当前窗口)"
echo -e " ${GREEN}✔${NC} ~/.bashrc 已清除代理块"
[[ -f "$ZSHRC_FILE" ]] && \
echo -e " ${GREEN}✔${NC} ~/.zshrc 已清除代理块"
echo -e " ${GREEN}✔${NC} apt 代理 已移除"
command -v git &>/dev/null && \
echo -e " ${GREEN}✔${NC} git 全局代理 已移除"
command -v docker &>/dev/null && \
echo -e " ${GREEN}✔${NC} docker 代理 已移除"
echo ""
}
# ── 子命令:ping ──────────────────────────────────────────────────────────────
cmd_ping() {
local target="${1:-https://www.google.com}"
local proxy_url="${_PROXY_CURRENT_URL:-}"
local curl_opts=()
if [[ -n "$proxy_url" ]]; then
info "代理已开启,经由 ${CYAN}${proxy_url}${NC} 测试: ${BOLD}${target}${NC}"
curl_opts=(--proxy "$proxy_url")
else
info "代理未开启,直连测试: ${BOLD}${target}${NC}"
fi
echo ""
local start end elapsed http_code
start=$(date +%s%3N)
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
"${curl_opts[@]}" \
--connect-timeout 10 \
--max-time 15 \
"$target" 2>&1) || true
end=$(date +%s%3N)
elapsed=$(( end - start ))
if [[ "$http_code" =~ ^[23] ]]; then
success "连通性: ${GREEN}正常${NC} HTTP ${http_code} 耗时 ${elapsed}ms"
elif [[ "$http_code" == "000" ]]; then
error "连通性: 失败(连接超时或目标不可达)"
exit 1
else
warn "连通性: 可到达但返回 HTTP ${http_code} 耗时 ${elapsed}ms"
fi
}
# ── 子命令:status ────────────────────────────────────────────────────────────
cmd_status() {
local current_url="${_PROXY_CURRENT_URL:-}"
echo ""
bold "═══ 代理状态检测 ═══"
echo ""
# 配置文件
if load_config 2>/dev/null; then
echo -e " 配置地址 ${GREEN}✔${NC} ${CYAN}${PROXY_HOST}:${PROXY_PORT}${NC} (${DIM}${CONFIG_FILE}${NC})"
else
echo -e " 配置地址 ${RED}✖${NC} 未配置(运行 proxy config <host>:<port>)"
fi
# 当前窗口
if [[ -n "$current_url" ]]; then
echo -e " 当前窗口 ${GREEN}✔${NC} ${CYAN}${current_url}${NC}"
else
echo -e " 当前窗口 ${DIM}—${NC} 未开启"
fi
# rc 文件
if _rc_has_proxy; then
echo -e " Shell RC ${GREEN}✔${NC} 代理已写入 ~/.bashrc(新终端自动生效)"
else
echo -e " Shell RC ${DIM}—${NC} 未设置"
fi
# apt
if _apt_has_proxy; then
echo -e " apt 代理 ${GREEN}✔${NC} ${DIM}${APT_PROXY_FILE}${NC}"
else
echo -e " apt 代理 ${DIM}—${NC} 未设置"
fi
# git
if _git_has_proxy; then
local gp
gp=$(git config --global --get http.proxy 2>/dev/null || echo "")
echo -e " git 代理 ${GREEN}✔${NC} ${CYAN}${gp}${NC}"
else
echo -e " git 代理 ${DIM}—${NC} 未设置"
fi
# docker
if _docker_has_proxy; then
echo -e " docker ${GREEN}✔${NC} ${DIM}${DOCKER_PROXY_FILE}${NC}"
else
echo -e " docker ${DIM}—${NC} 未设置"
fi
echo ""
}
# ── 子命令:config ────────────────────────────────────────────────────────────
cmd_config() {
local host="" port=""
load_config 2>/dev/null && {
host="${PROXY_HOST:-}"
port="${PROXY_PORT:-}"
} || true
if [[ $# -eq 0 ]]; then
echo ""
bold "═══ 代理配置向导 ═══"
echo ""
read -rp " 代理地址 (host:port,如 10.60.60.39:7890): " raw
_parse_and_save "$raw"
return
fi
case "$1" in
-host|--host)
[[ $# -ge 2 ]] || die "请提供 host 值: proxy config -host <host>"
host="$2"
validate_host "$host" || die "主机格式无效: $host"
[[ -n "$port" ]] || die "尚未设置 port,请先运行: proxy config <host>:<port>"
save_config "$host" "$port"
success "host 已更新: ${CYAN}${host}:${port}${NC}"
;;
-port|--port)
[[ $# -ge 2 ]] || die "请提供 port 值: proxy config -port <port>"
port="$2"
validate_port "$port" || die "端口无效(1-65535): $port"
[[ -n "$host" ]] || die "尚未设置 host,请先运行: proxy config <host>:<port>"
save_config "$host" "$port"
success "port 已更新: ${CYAN}${host}:${port}${NC}"
;;
*)
_parse_and_save "$1"
;;
esac
}
_parse_and_save() {
local raw="$1"
raw="${raw#http://}"; raw="${raw#https://}"; raw="${raw%/}"
if [[ "$raw" =~ ^(.+):([0-9]+)$ ]]; then
local h="${BASH_REMATCH[1]}" p="${BASH_REMATCH[2]}"
validate_host "$h" || die "主机格式无效: $h"
validate_port "$p" || die "端口无效(1-65535): $p"
save_config "$h" "$p"
success "代理已配置: ${CYAN}${h}:${p}${NC}"
info "运行 ${BOLD}proxy on${NC} 以启用代理"
else
die "格式错误,请使用 host:port,如: 10.60.60.39:7890"
fi
}
# ── 子命令:help ──────────────────────────────────────────────────────────────
cmd_help() {
echo ""
bold " proxy — Ubuntu 代理管理工具"
echo -e " ${DIM}版本 4.0 配置文件: ${CONFIG_FILE}${NC}"
echo ""
echo -e " ${BOLD}用法${NC}"
echo " proxy <命令> [选项]"
echo ""
echo -e " ${BOLD}命令${NC}"
echo ""
echo -e " ${CYAN}on${NC} 开启全局代理(Shell RC + apt + git + docker)"
echo ""
echo -e " ${CYAN}off${NC} 关闭全局代理(清除所有层级,保留配置地址)"
echo ""
echo -e " ${CYAN}ping${NC} [url] 测试连通性(根据当前代理状态决定是否走代理)"
echo -e " ${DIM}proxy ping (默认 https://www.google.com)${NC}"
echo -e " ${DIM}proxy ping https://www.youtube.com (指定测试地址)${NC}"
echo ""
echo -e " ${CYAN}status${NC} 显示代理状态(窗口 / RC / apt / git / docker)"
echo ""
echo -e " ${CYAN}config${NC} <host:port> 配置代理地址(不影响当前开启/关闭状态)"
echo -e " ${DIM}proxy config 10.60.60.39:7890 (一次设置 host 和 port)${NC}"
echo -e " ${DIM}proxy config -host 10.60.60.39 (单独更新 host)${NC}"
echo -e " ${DIM}proxy config -port 7890 (单独更新 port)${NC}"
echo -e " ${DIM}proxy config (交互式向导)${NC}"
echo ""
echo -e " ${CYAN}help${NC} [-h|--help] 显示此帮助"
echo ""
echo -e " ${BOLD}快速开始${NC}"
echo -e " ${DIM}1. proxy config 10.60.60.39:7890${NC}"
echo -e " ${DIM}2. proxy on${NC}"
echo -e " ${DIM}3. proxy ping${NC}"
echo -e " ${DIM}4. proxy off${NC}"
echo ""
}
# ── 入口 ──────────────────────────────────────────────────────────────────────
main() {
local cmd="${1:-help}"
shift || true
case "$cmd" in
on) cmd_on "$@" ;;
off) cmd_off ;;
ping) cmd_ping "$@" ;;
status) cmd_status ;;
config) cmd_config "$@" ;;
help|-h|--help) cmd_help ;;
*)
error "未知命令: $cmd"
cmd_help
exit 1
;;
esac
}
main "$@"
install_proxy.sh
#!/usr/bin/env bash
# =============================================================================
# install_proxy.sh — proxy 工具安装脚本
# 用法: sudo bash install_proxy.sh
# =============================================================================
set -euo pipefail
BIN_PATH="/usr/local/bin/_proxy_bin"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROXY_SCRIPT="$SCRIPT_DIR/proxy"
FUNC_MARKER_BEGIN="# >>> proxy-manager function <<<"
FUNC_MARKER_END="# <<< proxy-manager function <<<"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'
info() { echo -e "\033[0;34mℹ\033[0m $*"; }
success() { echo -e "${GREEN}✔${NC} $*"; }
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
error() { echo -e "${RED}✖${NC} $*" >&2; }
die() { error "$*"; exit 1; }
# ── 检查 root ─────────────────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
die "请使用 sudo 运行安装脚本: sudo bash install_proxy.sh"
fi
REAL_USER="${SUDO_USER:-$USER}"
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
BASHRC="$REAL_HOME/.bashrc"
ZSHRC="$REAL_HOME/.zshrc"
echo ""
echo -e "${BOLD} proxy 安装程序 v3.0${NC}"
echo -e " ${DIM}正在检查依赖...${NC}"
echo ""
# ── 检查依赖 ──────────────────────────────────────────────────────────────────
for dep in curl bash; do
if command -v "$dep" &>/dev/null; then
success "依赖 ${CYAN}${dep}${NC} 已安装"
else
die "缺少依赖: $dep (请先安装: sudo apt install $dep)"
fi
done
# ── 安装二进制 ────────────────────────────────────────────────────────────────
echo ""
info "安装到 ${CYAN}${BIN_PATH}${NC} ..."
[[ -f "$PROXY_SCRIPT" ]] || die "未找到 proxy 脚本: $PROXY_SCRIPT"
cp "$PROXY_SCRIPT" "$BIN_PATH"
chmod 755 "$BIN_PATH"
success "脚本已安装: ${CYAN}${BIN_PATH}${NC}"
# ── 注入 proxy shell function ─────────────────────────────────────────────────
# proxy function 的职责:
# 1. 将当前 shell 的代理状态(http_proxy)通过环境变量传给 _proxy_bin
# 2. 调用 _proxy_bin,捕获输出中的 __EVAL__: 行
# 3. 在当前 shell 中 eval 这些命令,实现环境变量的真正修改
FUNC_BLOCK=$(cat <<'FUNCEOF'
# >>> proxy-manager function <<<
proxy() {
local _output _line _eval_cmds=""
# 将当前窗口代理状态传入子进程,供 ping/status 读取
export _PROXY_CURRENT_URL="${http_proxy:-}"
# 捕获 _proxy_bin 的全部输出
_output=$(_proxy_bin "$@" 2>&1)
local _exit_code=$?
# 逐行处理:__EVAL__: 开头的行在当前 shell 执行,其余直接打印
while IFS= read -r _line; do
if [[ "$_line" == __EVAL__:* ]]; then
_eval_cmds="${_line#__EVAL__:}"
else
echo -e "$_line"
fi
done <<< "$_output"
# 在当前 shell 中执行环境变量操作
if [[ -n "$_eval_cmds" ]]; then
eval "$_eval_cmds"
# 同步更新传递给子进程的状态变量
export _PROXY_CURRENT_URL="${http_proxy:-}"
fi
unset _PROXY_CURRENT_URL
return $_exit_code
}
# <<< proxy-manager function <<<
FUNCEOF
)
_inject_function() {
local rcfile="$1"
[[ -f "$rcfile" ]] || return 0
# 清除旧版注入
sed -i "/# >>> proxy-manager function <<</,/# <<< proxy-manager function <<</d" "$rcfile"
# 同时清除旧版 v1/v2 的 short 标记(兼容旧安装)
sed -i "/# >>> proxy-manager short <<</,/# <<< proxy-manager short <<</d" "$rcfile"
sed -i "/# >>> proxy-manager global <<</,/# <<< proxy-manager global <<</d" "$rcfile"
echo "$FUNC_BLOCK" >> "$rcfile"
chown "$REAL_USER:$(id -gn "$REAL_USER")" "$rcfile"
}
echo ""
info "注入 proxy shell function ..."
_inject_function "$BASHRC"
success "已写入 ${CYAN}${BASHRC}${NC}"
if [[ -f "$ZSHRC" ]]; then
_inject_function "$ZSHRC"
success "已写入 ${CYAN}${ZSHRC}${NC}"
fi
# ── 首次配置向导 ──────────────────────────────────────────────────────────────
echo ""
echo -e " ${BOLD}首次配置${NC}"
echo ""
CONFIG_FILE="$REAL_HOME/.config/proxy-manager/config"
if [[ -f "$CONFIG_FILE" ]]; then
warn "检测到已有配置文件,跳过首次配置向导"
info "如需修改代理地址,请运行: proxy config <host>:<port>"
else
read -rp " 请输入代理地址 (host:port,如 10.60.60.39:7890,回车跳过): " raw
if [[ -n "$raw" ]]; then
sudo -u "$REAL_USER" "$BIN_PATH" config "$raw" || \
warn "配置写入失败,请安装后手动运行: proxy config <host>:<port>"
else
warn "跳过配置,请稍后手动运行: proxy config <host>:<port>"
fi
fi
# ── 完成 ──────────────────────────────────────────────────────────────────────
echo ""
echo -e " ${GREEN}${BOLD}安装完成!${NC}"
echo ""
echo -e " ${BOLD}⚠ 重要:${NC}请执行以下命令使 proxy function 在当前终端生效:"
echo -e " ${CYAN}source ~/.bashrc${NC}"
echo ""
echo -e " ${BOLD}快速上手${NC}"
echo -e " ${DIM}proxy config 10.60.60.39:7890 # 配置代理地址${NC}"
echo -e " ${DIM}proxy on # 开启全局代理${NC}"
echo -e " ${DIM}proxy ping # 测试连通性${NC}"
echo -e " ${DIM}proxy status # 查看状态${NC}"
echo -e " ${DIM}proxy off # 关闭代理${NC}"
echo -e " ${DIM}proxy -h # 查看帮助${NC}"
echo ""