🛰️ proxy ------ Ubuntu 全局代理管理工具
一个 Shell 脚本,解决 Ubuntu 代理设置散乱、切换繁琐的痛点。安装后注册为全局命令,统一管理代理的开启、关闭、配置与状态检测。
背景:为什么需要这个工具?
在 Ubuntu 上使用代理一直是一件「能用但麻烦」的事。你可能遇到过这些场景:
- 每次开机都要手动
export http_proxy=...,关掉终端就失效了 - 忘记到底改了哪些配置文件,代理设置「幽灵般」时有时无
- 想临时测试代理是否连通,只能
curl --proxy手打一长串 - 同事问你「代理开了没」,你自己都不确定当前状态
于是我写了 proxy------一个单文件 Shell 脚本,安装后注册为全局命令,统一管理代理的开启、关闭、配置和状态检测。
功能速览
| 命令 | 说明 |
|---|---|
proxy config 10.x.x.x:7890 |
配置代理地址(支持交互式向导) |
proxy on |
开启代理(默认写入 ~/.bashrc) |
proxy on -short |
写入 ~/.bashrc,当前用户,重启后仍有效 |
proxy on -long |
写入 /etc/profile.d/,系统级持久(需 sudo) |
proxy off |
关闭所有层级代理 |
proxy ping |
测试代理连通性(默认 google.com) |
proxy ping <url> |
测试指定地址连通性 |
proxy status |
查看三层代理状态 |
proxy -h |
帮助信息 |
安装
前置依赖
bash:Ubuntu 默认已安装curl:用于proxy ping连通性测试(sudo apt install curl)sudo 权限:仅proxy on -long和安装时需要
一键安装
将 proxy 和 install_proxy.sh 两个文件放在同一目录,执行:
bash
chmod +x proxy install_proxy.sh
sudo bash install_proxy.sh
安装脚本会自动:
- 检查
curl/bash依赖是否存在 - 将
proxy复制到/usr/local/bin/,注册为全局命令 - 引导你完成首次代理地址配置(可回车跳过)
✅ 提示 :安装完成后无需重新登录,
proxy命令立即可用。
使用指南
第一步:配置代理地址
支持三种配置方式,灵活应对不同场景:
arduino
# 一次性设置 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 提供两种持久级别,按需选择:
用户级(-short,默认)
vbnet
proxy on # 等同于 proxy on -short
proxy on -short
写入 ~/.bashrc(以及 ~/.zshrc,如果存在)。新开终端自动生效,重启系统后仍有效,但只影响当前用户。
系统级(-long)
csharp
proxy on -long
写入 /etc/profile.d/proxy-manager.sh,对所有用户生效,重新登录后系统级代理自动激活。需要 sudo 权限。
⚠️ 注意 :
proxy on会同时导出环境变量到当前 shell 进程。新开终端会从 rc 文件中读取,无需再次执行。
关闭代理
vbnet
proxy off
一条命令同时清除三个层级的代理设置:
- 清除当前 shell 的环境变量(
unset) - 从
~/.bashrc和~/.zshrc中移除代理块 - 删除
/etc/profile.d/proxy-manager.sh(如果存在)
测试连通性
bash
proxy ping # 默认测试 https://www.google.com
proxy ping https://www.youtube.com # 指定测试地址
示例输出:
perl
ℹ 使用代理 http://10.60.60.39:7890 测试连接: https://www.google.com
✔ 连通性: 正常 HTTP 200 耗时 342ms
ping 子命令会显示 HTTP 状态码和耗时,帮助你快速判断代理质量,而不是只告诉你「通了」或「没通」。
查看状态
lua
proxy status
示例输出:
arduino
═══ 代理状态检测 ═══
配置文件 ✔ 10.60.60.39:7890 (~/.config/proxy-manager/config)
系统级 --- 未设置
用户RC ✔ 代理已写入 ~/.bashrc
当前Shell ✔ http://10.60.60.39:7890
状态分三层显示:配置文件(地址是否已保存)、系统级(/etc/profile.d 是否有配置)、当前 Shell(环境变量是否已生效)。一眼定位问题所在。
设计决策与实现细节
为什么分「用户级」和「系统级」?
Linux 代理环境变量没有统一的系统管理机制,不同应用读取的来源各不相同。脚本采用分层策略:
-short:只改当前用户的 rc 文件,不影响其他用户,适合个人开发机-long:写入/etc/profile.d/,所有登录用户都能读到,适合共享服务器或 CI 环境
配置文件安全性
配置文件路径为 ~/.config/proxy-manager/config,创建时自动设置权限为 600(仅当前用户可读写)。文件格式为简单的 key=value,通过 source 加载,不会执行任意代码。
代理格式校验
config 子命令会对输入进行格式校验,拒绝非法的 host 和 port:
- host :允许 IPv4 地址、合法域名、
localhost - port:必须为纯数字,范围 1--65535
- 支持输入时带
http://或https://前缀,自动剥除
off 的幂等性
proxy off 可以反复执行而不会报错。即使某一层的配置不存在(比如从未执行过 proxy on -long),脚本也会静默跳过,不会因为文件不存在而中断。
完整流程示例
csharp
# 1. 首次配置代理地址
proxy config 10.60.60.39:7890
# ✔ 代理已配置: 10.60.60.39:7890
# 2. 开启代理(用户级)
proxy on
# ✔ 代理已开启(当前 shell + rc 文件): http://10.60.60.39:7890
# 3. 验证连通性
proxy ping
# ✔ 连通性: 正常 HTTP 200 耗时 312ms
# 4. 查看状态
proxy status
# 配置文件 ✔ 10.60.60.39:7890
# 用户RC ✔ 代理已写入 ~/.bashrc
# 当前Shell ✔ http://10.60.60.39:7890
# 5. 修改代理服务器(只改 host)
proxy config -host 10.60.60.100
proxy on # 重新激活
# 6. 关闭代理
proxy off
# ✔ 代理已关闭
获取脚本
完整源码包含两个文件:
proxy--- 主脚本,安装后作为全局命令使用install_proxy.sh--- 安装脚本,需要sudo运行
proxy
bash
#!/usr/bin/env bash
# =============================================================================
# proxy - Ubuntu 全局代理管理工具
# 安装: sudo bash install_proxy.sh
# 用法: proxy <command> [options]
# =============================================================================
set -euo pipefail
# ── 常量 ─────────────────────────────────────────────────────────────────────
SCRIPT_NAME="proxy"
CONFIG_FILE="$HOME/.config/proxy-manager/config"
PROFILE_FILE="/etc/profile.d/proxy-manager.sh"
BASHRC_MARKER="# >>> proxy-manager short <<<"
BASHRC_FILE="$HOME/.bashrc"
ZSHRC_FILE="$HOME/.zshrc"
# 颜色
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}"; }
# ── 配置读写 ──────────────────────────────────────────────────────────────────
config_dir() { dirname "$CONFIG_FILE"; }
load_config() {
if [[ ! -f "$CONFIG_FILE" ]]; then
return 1
fi
# shellcheck source=/dev/null
source "$CONFIG_FILE"
return 0
}
save_config() {
local host="$1" port="$2"
mkdir -p "$(config_dir)"
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"
# 允许 IPv4、域名、localhost
if [[ "$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" ]]; then
return 0
fi
return 1
}
validate_port() {
local port="$1"
[[ "$port" =~ ^[0-9]+$ ]] && (( port >= 1 && port <= 65535 ))
}
# ── 写入环境变量 ──────────────────────────────────────────────────────────────
# 生成 export 语句块
_proxy_exports() {
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
}
_proxy_unsets() {
cat <<EOF
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY \
ftp_proxy FTP_PROXY no_proxy NO_PROXY
EOF
}
# 持久化写入 /etc/profile.d(需要 sudo)
_write_long_on() {
local url="$1"
sudo tee "$PROFILE_FILE" > /dev/null <<EOF
# proxy-manager --- 持久代理设置(proxy on -long)
$(_proxy_exports "$url")
EOF
sudo chmod 644 "$PROFILE_FILE"
}
_remove_long() {
if [[ -f "$PROFILE_FILE" ]]; then
sudo rm -f "$PROFILE_FILE"
fi
}
# 写入 ~/.bashrc / ~/.zshrc(短期,重启 shell 后仍有效但非系统级)
_write_short_on() {
local url="$1"
# 先清除旧标记
_remove_short_from_rc "$BASHRC_FILE"
[[ -f "$ZSHRC_FILE" ]] && _remove_short_from_rc "$ZSHRC_FILE"
local block
block="$(cat <<EOF
${BASHRC_MARKER}
$(_proxy_exports "$url")
# <<< proxy-manager short <<<
EOF
)"
echo "$block" >> "$BASHRC_FILE"
[[ -f "$ZSHRC_FILE" ]] && echo "$block" >> "$ZSHRC_FILE"
}
_remove_short_from_rc() {
local file="$1"
[[ -f "$file" ]] || return 0
# 删除标记块
sed -i '/# >>> proxy-manager short <<</,/# <<< proxy-manager short <<</d' "$file"
}
_remove_short() {
_remove_short_from_rc "$BASHRC_FILE"
[[ -f "$ZSHRC_FILE" ]] && _remove_short_from_rc "$ZSHRC_FILE"
}
# ── 子命令实现 ────────────────────────────────────────────────────────────────
cmd_on() {
local mode="${1:--short}" # 默认 -short
local proxy_url
proxy_url="$(get_proxy_url)"
case "$mode" in
-short|short)
# 当前 shell 直接生效(eval 方式)
_write_short_on "$proxy_url"
# 同时导出到当前进程
eval "$(_proxy_exports "$proxy_url")"
success "代理已开启(当前 shell + rc 文件): ${CYAN}${proxy_url}${NC}"
warn "新开终端会自动读取 ~/.bashrc,关闭后不影响系统其他用户"
info "若希望当前 shell 立即生效,还需执行: ${DIM}source ~/.bashrc${NC}"
;;
-long|long)
_write_long_on "$proxy_url"
_write_short_on "$proxy_url"
success "代理已持久化开启(系统级): ${CYAN}${proxy_url}${NC}"
info "重新登录或新开终端后对所有用户生效(写入 /etc/profile.d/)"
;;
*)
die "未知选项: $mode (可用: -short / -long)"
;;
esac
}
cmd_off() {
_remove_long
_remove_short
eval "$(_proxy_unsets)"
success "代理已关闭(已从 rc 文件和系统级配置中移除)"
info "当前 shell 环境变量已清除;新终端打开后将不再使用代理"
}
cmd_ping() {
local target="${1:-https://www.google.com}"
local proxy_url
proxy_url="$(get_proxy_url)"
info "使用代理 ${CYAN}${proxy_url}${NC} 测试连接: ${BOLD}${target}${NC}"
echo ""
local start end elapsed http_code
start=$(date +%s%3N)
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
--proxy "$proxy_url" \
--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
}
cmd_status() {
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 [[ -f "$PROFILE_FILE" ]]; then
echo -e " 系统级 ${GREEN}✔${NC} 持久代理已启用 (${DIM}${PROFILE_FILE}${NC})"
else
echo -e " 系统级 ${DIM}---${NC} 未设置"
fi
# RC 文件(短期持久)
if grep -q "proxy-manager short" "$BASHRC_FILE" 2>/dev/null; then
echo -e " 用户RC ${GREEN}✔${NC} 代理已写入 ~/.bashrc"
else
echo -e " 用户RC ${DIM}---${NC} 未设置"
fi
# 当前 shell 环境变量
if [[ -n "${http_proxy:-}${HTTP_PROXY:-}" ]]; then
local current="${http_proxy:-${HTTP_PROXY:-}}"
echo -e " 当前Shell ${GREEN}✔${NC} ${CYAN}${current}${NC}"
else
echo -e " 当前Shell ${DIM}---${NC} 无代理环境变量"
fi
echo ""
}
cmd_config() {
# proxy config 10.60.60.39:7890
# proxy config -host 10.60.60.39
# proxy config -port 7890
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>"
;;
-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>"
;;
*)
_parse_and_save "$1"
return
;;
esac
save_config "$host" "$port"
success "代理已配置: ${CYAN}${host}:${port}${NC}"
info "运行 ${BOLD}proxy on${NC} 以启用代理"
}
_parse_and_save() {
local raw="$1"
# 去掉协议头
raw="${raw#http://}"
raw="${raw#https://}"
raw="${raw%/}"
if [[ "$raw" =~ ^(.+):([0-9]+)$ ]]; then
local h="${BASH_REMATCH[1]}"
local 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
}
cmd_help() {
echo ""
bold " proxy --- Ubuntu 代理管理工具"
echo -e " ${DIM}版本 1.0 配置文件: ${CONFIG_FILE}${NC}"
echo ""
echo -e " ${BOLD}用法${NC}"
echo " proxy <命令> [选项]"
echo ""
echo -e " ${BOLD}命令${NC}"
echo ""
echo -e " ${CYAN}on${NC} [-short|-long] 开启代理"
echo -e " ${DIM}proxy on (默认 -short)${NC}"
echo -e " ${DIM}proxy on -short 写入 ~/.bashrc,当前用户,重启后有效${NC}"
echo -e " ${DIM}proxy on -long 写入 /etc/profile.d/,系统级持久(需要 sudo)${NC}"
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} 显示当前代理状态(配置、系统级、当前shell)"
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 ""
}
# ── 入口 ──────────────────────────────────────────────────────────────────────
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
bash
#!/usr/bin/env bash
# =============================================================================
# install_proxy.sh --- proxy 工具安装脚本
# 用法: sudo bash install_proxy.sh
# =============================================================================
set -euo pipefail
INSTALL_PATH="/usr/local/bin/proxy"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROXY_SCRIPT="$SCRIPT_DIR/proxy"
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
# ── 检查依赖 ──────────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD} proxy 安装程序${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}${INSTALL_PATH}${NC} ..."
if [[ ! -f "$PROXY_SCRIPT" ]]; then
die "未找到 proxy 脚本文件: $PROXY_SCRIPT (请确保 proxy 和 install_proxy.sh 在同一目录)"
fi
cp "$PROXY_SCRIPT" "$INSTALL_PATH"
chmod 755 "$INSTALL_PATH"
success "脚本已安装: ${CYAN}${INSTALL_PATH}${NC}"
# ── 首次配置向导 ──────────────────────────────────────────────────────────────
echo ""
echo -e " ${BOLD}首次配置${NC}"
echo ""
CONFIG_FILE="$HOME/.config/proxy-manager/config"
# 如果以 sudo 运行,CONFIG_FILE 应写入原始用户的 home
REAL_USER="${SUDO_USER:-$USER}"
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
CONFIG_FILE="$REAL_HOME/.config/proxy-manager/config"
if [[ -f "$CONFIG_FILE" ]]; then
warn "已检测到现有配置文件,跳过首次配置向导"
info "如需修改代理地址,请运行: ${BOLD}proxy config <host>:<port>${NC}"
else
read -rp " 请输入代理地址 (host:port,如 10.60.60.39:7890,回车跳过): " raw
if [[ -n "$raw" ]]; then
# 以原始用户身份执行配置
sudo -u "$REAL_USER" "$INSTALL_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}"
echo -e " ${DIM}proxy config 10.60.60.39:7890 # 配置代理地址${NC}"
echo -e " ${DIM}proxy on # 开启代理(当前用户)${NC}"
echo -e " ${DIM}proxy on -long # 开启系统级持久代理${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 ""