4. 排查工具与命令——Windows下set/echo命令、Linux下env/printenv/export命令、跨平台工具envsubst的使用
环境变量冲突排查,说白了就是一场「找不同」游戏。你得知道当前系统里到底有哪些变量,它们的值是什么,从哪来的。没有趁手的工具,光靠肉眼翻注册表或者 /etc/profile,那效率太低了。
这一章,我把日常排查中最常用的命令给你捋一遍。Windows 和 Linux 我都会讲到,最后再介绍一个跨平台的利器 envsubst。这些命令我几乎每天都会敲,闭着眼都能打出来。
4.1 Windows 下的排查命令
Windows 的环境变量机制比 Linux 要「黑盒」一些。它分系统级、用户级,还有进程级。排查时,我习惯从最直观的命令入手。
4.1.1 set —— 查看当前会话的所有变量
在 cmd 里直接敲 set,不加任何参数,它会列出当前命令行窗口(也就是当前进程)继承到的所有环境变量。
C:\Users\admin> set
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\admin\AppData\Roaming
JAVA_HOME=C:\Program Files\Java\jdk-17
PATH=C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk-17\bin;...
set PATH 只过滤出 PATH 变量,避免被几百行输出淹没。命令是 set PATH,注意中间没有等号。
4.1.2 echo %变量名% —— 精准定位单个变量
如果你只想看某个变量,比如 JAVA_HOME,用 echo 加百分号是最快的。
C:\Users\admin> echo %JAVA_HOME%
C:\Program Files\Java\jdk-17
嗯,这里要注意一个坑。如果变量名写错了,或者变量根本没定义,Windows 不会报错,它会原样输出 %JAVA_HOME% 这个字符串。我曾经排查过一个 Tomcat 启动失败的问题,折腾了半天,最后发现是 %JAVA_HOME% 后面多了一个空格,导致路径拼接错了。
echo %JAVA_HOME% 调试,输出看起来正常,但实际路径末尾带了一个不可见字符。建议用 set JAVA_HOME 来验证,它会显示完整的键值对,包括空格。
4.1.3 查看系统级与用户级变量
cmd 里的 set 只能看到当前进程的变量。如果你想看系统级和用户级的「原始配置」,得去注册表或者用 setx 命令。不过我个人更推荐用 GUI——右键「此电脑」→「属性」→「高级系统设置」→「环境变量」。虽然看起来不够「极客」,但胜在直观,不容易漏看。
4.2 Linux 下的排查命令
Linux 的环境变量体系比 Windows 透明得多。你想想看,一切皆文件,环境变量也不例外。排查起来工具链非常清晰。
4.2.1 env —— 列出所有环境变量
env 是最干净的命令。它只显示环境变量,不包含 shell 内部的局部变量(比如你在脚本里用 var=value 定义的,没有 export 的那种)。
$ env
HOME=/home/admin
SHELL=/bin/bash
JAVA_HOME=/usr/lib/jvm/java-17-openjdk
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
4.2.2 printenv —— 与 env 类似,但支持参数
printenv 和 env 输出基本一样。但它有一个好处:可以指定变量名。
$ printenv JAVA_HOME
/usr/lib/jvm/java-17-openjdk
我个人习惯用 printenv 来查单个变量,因为不用加 $ 符号,少敲一下键盘。
4.2.3 export —— 查看已导出的变量
在 bash 里直接敲 export,效果和 env 差不多。但它真正的用途是设置变量并传递给子进程。排查时,我常用它来临时修改变量,测试问题是否复现。
$ export JAVA_HOME=/opt/jdk-11
$ java -version
# 此时 java 会使用新的 JAVA_HOME
env 和 printenv 是「只读」的,export 可以「写」。排查时先用 env 看现状,再用 export 做临时修复。
4.2.4 echo $变量名 —— 最直接的查看方式
这个不用多说了,和 Windows 的 echo %变量名% 一个道理。但 Linux 下要注意引号的问题。
$ echo $JAVA_HOME
/usr/lib/jvm/java-17-openjdk
$ echo "$JAVA_HOME"
/usr/lib/jvm/java-17-openjdk
$ echo '$JAVA_HOME'
$JAVA_HOME # 单引号会阻止变量展开
我曾经在写启动脚本时,用单引号包裹了 $JAVA_HOME,结果传进去的是字符串 $JAVA_HOME 而不是路径,应用直接报错找不到 Java。这个坑,你想想看,排查起来多隐蔽。
4.3 跨平台工具:envsubst
前面讲的都是查看命令。但实际工作中,我们经常需要把环境变量替换到配置文件中。比如你有一个 application.template.yaml,里面写了 db.url: ${DB_URL},部署时得把 ${DB_URL} 替换成真实值。
这时候 envsubst 就派上用场了。它是 GNU gettext 工具集里的一员,在 Linux 上基本是标配。Windows 下可以通过 Git Bash 或者 WSL 使用。
4.3.1 基本用法
$ export DB_URL=jdbc:mysql://localhost:3306/mydb
$ export DB_USER=root
$ cat application.template.yaml
db:
url: ${DB_URL}
user: ${DB_USER}
$ envsubst < application.template.yaml > application.yaml
$ cat application.yaml
db:
url: jdbc:mysql://localhost:3306/mydb
user: root
4.3.2 只替换指定的变量
默认情况下,envsubst 会替换所有 ${VAR} 格式的变量。如果你只想替换某几个,可以用 $VARIABLE 作为参数。
$ envsubst '$DB_URL $DB_USER' < application.template.yaml > application.yaml
这样做的好处是,避免把模板里其他 ${} 格式的内容(比如 log4j 的占位符)也给误替换了。我在做微服务配置中心迁移时,就靠这个参数避免了大量配置错误。
envsubst 只识别 ${VAR} 格式,不识别 $VAR(不带花括号)。如果你的模板里写的是 $DB_URL,它不会替换。我建议团队统一规范,都用 ${VAR} 格式。
4.4 本章知识体系总览
下面这张图,我把本章的核心命令和它们的关系梳理了一下。你可以把它当作一个速查地图。
4.5 命令对比速查表
为了方便你快速查阅,我把这几个命令的核心差异整理成了表格。排查时直接看这张表就够了。
| 命令 | 平台 | 作用 | 典型场景 |
|---|---|---|---|
set |
Windows | 列出当前进程所有变量 | 排查 PATH 冲突、查看继承的变量 |
echo %VAR% |
Windows | 查看单个变量值 | 快速验证变量是否生效 |
env |
Linux | 列出所有环境变量 | 查看系统级变量、排查启动脚本 |
printenv |
Linux | 查看单个或全部环境变量 | 精准查询某个变量 |
export |
Linux | 设置/查看已导出的变量 | 临时修改变量、测试启动参数 |
echo $VAR |
Linux | 查看单个变量值 | 脚本调试、快速验证 |
envsubst |
跨平台 | 替换模板中的环境变量 | 配置生成、容器化部署 |