背景
访问 GitHub 的网络连接时通时断,GitHub CLI(gh)经常超时。git 可以按域名设置代理(http.<url>.proxy,只对 github.com 生效),gh 却只认 HTTPS_PROXY 环境变量。而 export HTTPS_PROXY 是全局的:shell 里所有命令的请求都会经过这个代理,包括公司内网仓库、gitee 等在内,代理服务不可用时这些命令全部失败。
需求是:只有 gh 经过代理,其余命令不受影响。
一行解法
bash
gh() { HTTPS_PROXY=http://127.0.0.1:7890 HTTP_PROXY=http://127.0.0.1:7890 command gh "$@"; }
拆开看四个部分:
| 片段 | 作用 |
|---|---|
gh() { ...; } |
定义与命令同名的包装函数:敲 gh 时优先命中函数,而不是去 PATH 找原始程序 |
VAR=值 写在命令前 |
单命令环境变量:只对紧跟的这条命令的进程生效。不是 export,不留在 Shell 里,也不传递给其他命令 |
command gh |
内置关键字,绕过函数直接调用原始程序,避免函数调用自己造成无限递归 |
"$@" |
原样透传所有参数,每个参数独立引号包裹 |
验证方法:先 export HTTPS_PROXY=http://127.0.0.1:9(一个故意写错的地址)再调用 gh,依然能成功,说明函数内部的赋值覆盖了外部变量;调用后执行 env | grep -i proxy,没有输出,说明变量没有泄漏到 Shell 环境。
覆盖两个世界
bash 读取配置的规则按启动模式分成两条互不相通的通道:
- 交互 shell(手动打开的终端):读取
~/.bashrc,不读BASH_ENV - 非交互 shell(脚本、AI 编程会话中执行的命令):只读
BASH_ENV指向的文件,不读~/.bashrc
完整的做法是一处定义、两处引用:
bash
# ~/.bash_env.sh ------ 函数唯一定义处,保持极简
export PATH="/c/Program Files/GitHub CLI:$PATH"
gh() { HTTPS_PROXY=http://127.0.0.1:7890 HTTP_PROXY=http://127.0.0.1:7890 command gh "$@"; }
bash
# 设置用户环境变量,全机所有非交互 bash 启动时自动加载上面的文件
setx BASH_ENV "C:/Users/you/.bash_env.sh"
# 在 ~/.bashrc 末尾引用同一个文件,交互 shell 由此获得同一个函数,保持单一来源
echo '. ~/.bash_env.sh' >> ~/.bashrc
git 原有的按域名代理配置不受影响,gh 与 git 各自使用各自的规则。
其他适用场景
凡是"只想让某个命令带上特殊环境变量"的场景,都可以用同样的写法:
bash
mvn() { JAVA_HOME=/path/to/jdk8 command mvn "$@"; } # 老项目固定 JDK8
node() { NODE_OPTIONS=--enable-source-maps command node "$@"; } # 单命令开启 source maps
docker() { DOCKER_HOST=ssh://dev-box command docker "$@"; } # 指定远程 docker
不再需要时删掉函数即可,没有残留。
两点注意
- 函数体内只用"单命令环境变量"写法,不要写
export,否则作用域会从单个进程扩大到整个 Shell; BASH_ENV指向的文件会在全机每个非交互脚本执行前被加载,内容应保持极简,不要放入 sdkman 初始化这类较重的逻辑。
小结
包装函数加单命令环境变量,可以把一份配置的作用域限定到单个命令的进程:需要它的命令拿到它,其他命令不受影响。
(END)