环境变量、代理与 DNS
装完软件敲命令却提示找不到,或者浏览器能打开、命令行打不开,这两类怪事多半出在同三个地方。
- 环境变量到底是什么,为什么有些程序离开它就找不到东西;
- PATH 为什么单独拿出来讲,它和别的变量的地位不一样在哪;
- 刚设完环境变量,为什么当前窗口还是不生效;
- DNS 在做什么,代理在做什么,两者谁先谁后;
- 网络不通时,怎么用三条命令判断卡在哪一段。
一、环境变量是一张随程序附带的便条
你在终端里敲 python,系统需要知道两件事:去哪个目录找这个程序,以及这个程序运行时该读哪些配置。第二件事靠的就是环境变量。
把它想成一张便条:启动一个程序时,系统会把这张便条一起递过去。程序自己决定读便条上的哪几行,不读也行。
二、便条上的常见几行
零基础阶段真正会打交道的变量只有几个,先把它们认全。
| 变量名 | 管什么 | 写错时的典型症状 |
|---|---|---|
PATH | 敲命令时去哪几个目录找可执行文件,按顺序找,找到就停 | 提示「找不到命令」或「不是内部或外部命令」,但文件明明存在 |
HOME / USERPROFILE | 你的用户主目录在哪。macOS 与 Linux 用前一个,Windows 用后一个 | 程序把配置写到了别处,或者报「无法创建配置文件」 |
TEMP / TMPDIR | 临时文件放哪 | 安装到一半报「磁盘不可写」,其实是临时目录没有权限 |
LANG / LC_ALL | 程序输出用什么语言、什么编码 | 中文变问号,或者日志里全是英文你读不懂 |
SHELL | 当前用的是哪个终端程序 | 写在配置文件里的语法另一套终端不认识 |
还有一类是某个软件自己认的变量,比如数据库的地址、某个工具的密钥存放位置。这类变量的名字由那个软件规定,得去它的文档里查,不要猜。
三、PATH 为什么单独拿出来讲
别的变量负责「配置」,PATH 负责「路由」。程序怎么运行由前者决定,敲下命令时系统去找哪个程序由后者决定。
PATH 的值是一串目录,用冒号隔开(Windows 上用分号)。系统从左到右依次找,第一个找到的胜出,后面的目录不再看。
flowchart LR C["你敲下 python"] --> P["读 PATH 这张目录清单"] P --> D1["目录一 有没有 python"] D1 -->|"有"| RUN["执行这个文件"] D1 -->|"没有"| D2["目录二 有没有 python"] D2 -->|"有"| RUN D2 -->|"没有"| NF["继续往后找"] NF -->|"全都没有"| ERR["报找不到命令"]
python 跑起来的永远是 PATH 里靠前的那份。
遇到版本对不上,第一件事是先看它到底跑了哪个文件,别急着重装。
四、看一眼当前的值
下面三条命令在 macOS 与 Linux 上都能用。Windows 的两条在 PowerShell 里跑。
macOS 与 Linux:
echo $PATH # 看 PATH 这张清单
which python # 看 python 落在了哪个文件上
env | sort # 列出全部环境变量,按名字排序
Windows 的 PowerShell:
$env:PATH # 看 PATH 这张清单
Get-Command python # 看 python 落在了哪个文件上
Get-ChildItem Env: # 列出全部环境变量
which 这一条最值得养成习惯。它把「我以为」换成「实际是」。版本冲突、命令莫名奇妙不生效,先跑它。
五、设置之后为什么当前窗口还不生效
环境变量不像全局共享的一块内存,它是在启动进程时被复制一份递过去的。你改了「源头」,已经开着的窗口拿到的还是旧的那份。
- 改完系统里的设置,要重新打开一个终端窗口,新窗口才拿到新的值。
- 在终端里用
export设的值,只对当前窗口有效。关掉窗口就没了。 - 想让每次打开都生效,得写进启动脚本。macOS 与 Linux 通常是
~/.zshrc或~/.bashrc,Windows 是系统设置里的「环境变量」面板。 - 写进启动脚本之后,也要重开窗口,或者用
source ~/.zshrc让当前窗口重读一遍。
echo $PATH,看新加的目录在不在里面。不在,说明文件根本没被读到,或者那条路径写错了。
六、DNS 在做什么
你输入的是名字,网络实际传输要靠地址。把名字换成地址这件事,由 DNS 负责。
过程很短:程序问系统「这个域名对应哪个地址」,系统去看自己的缓存,缓存没有就去问配置好的 DNS 服务器,拿到结果后告诉程序,顺便缓存一会儿。
| 环节 | 它做什么 | 出错时的症状 |
|---|---|---|
| 本地缓存 | 记住最近查过的结果,省一次网络往返 | 对方换了地址,你还在用旧地址,表现为「只有你的电脑打不开」 |
| DNS 服务器 | 真正回答「名字对应哪个地址」 | 所有域名都打不开,提示「找不到服务器」或「域名解析失败」 |
| hosts 文件 | 本机手写的对照表,优先级高于 DNS | 某一条被改过,那个域名会固定指向一个地址,别的域名正常 |
排查顺序就是从近到远:先清本地缓存,再看 hosts 文件有没有被改过,最后才怀疑 DNS 服务器。
macOS 与 Linux 上清缓存与查 hosts:
cat /etc/hosts # 看本机对照表
sudo dscacheutil -flushcache # macOS 清 DNS 缓存
sudo systemd-resolve --flush-caches # 部分 Linux 发行版
Windows 的 PowerShell:
notepad C:\Windows\System32\drivers\etc\hosts # 看本机对照表
ipconfig /flushdns # 清 DNS 缓存
nslookup example.com # 直接问一次 DNS
七、代理在做什么
代理是替你转发请求的中间人。程序把请求交给代理,代理替它去连目标,再把结果带回来。
它和 DNS 的分工可以这样分:DNS 解决「这个名字对应哪个地址」,代理解决「这次连接由谁替我发出去」。两件事互不替代,都可能单独出问题。
flowchart LR APP["你的程序"] --> Q["这次连接要不要走代理"] Q -->|"不走"| DNS1["查 DNS 拿地址"] Q -->|"走"| PX["把请求交给代理"] PX --> DNS2["代理那边查 DNS 拿地址"] DNS1 --> T["连到目标"] DNS2 --> T
八、两条命令看清代理设置
代理设置也走环境变量,最常见的三个名字是 http_proxy、https_proxy、no_proxy。前两个写代理的地址,最后一个写「这些地址不要走代理」。
macOS 与 Linux:
echo $http_proxy $https_proxy $no_proxy
curl -v https://example.com 2>&1 | head -20 # 看它连的是谁
Windows 的 PowerShell:
$env:HTTP_PROXY; $env:HTTPS_PROXY; $env:NO_PROXY
curl.exe -v https://example.com 2>&1 | Select-Object -First 20
curl -v 的输出里有 Trying 1.2.3.4:443 这一行。它连的地址是不是你以为的那个,一眼就能看出来。这一行比任何猜测都可靠。
no_proxy 里写本机地址时,写 localhost 往往不够,还要写 127.0.0.1。
少写一个,本机服务也跟着走代理,表现是「本地起的服务明明在跑,却连不上」。
九、三条命令定位卡在哪一段
网络不通时,不要一上来就重装或改 DNS。按顺序问三个问题,每一步都能把范围砍掉一半。
- 名字能不能换成地址? 用
nslookup或ping试一个你确定存在过的域名。换不出来,问题在 DNS 或 hosts。 - 地址能不能连上? 拿到地址之后直接连那个地址。用地址能连、用名字连不上,问题还在 DNS 那一段。
- 连的是不是你以为的那个地方? 用
curl -v看它实际连到哪。连到了一个不认识的地址,问题在代理设置。
echo $PATH、which python、env | sort。
然后用 curl -v 访问一个你常用的网站,在输出里找到 Trying 那一行,看它连的地址对不对。
三分钟,你会对自己这台机器的网络配置有一次具体的认识。
十、小结不了的东西
这一页讲的三样东西,共同点是「看不见、又在生效」。它们出问题时不会报一个明确的错,只会表现成「明明装了却用不了」「明明能上网却打不开」。
所以对付它们的办法是先把隐藏的东西显出来:echo 出变量、which 出真实路径、curl -v 出真实连接。看见之后再判断,比凭感觉调设置快得多。
1. 装完一个工具,提示「找不到命令」,但文件确实在硬盘上。先查什么?
which 或 Get-Command 查,输出为空就说明 PATH 没包含那个目录,或者新加的值还没被当前窗口读到。重装通常解决不了这一类问题。2. 刚在系统设置里改了 PATH,当前窗口仍然报旧的结果,为什么?
3. 浏览器能打开某个网站,命令行里同一个命令却连不上。最可能的原因是什么?
http_proxy 这类变量。浏览器读系统代理设置,终端程序读环境变量,这是两套入口。4. DNS 出问题和代理出问题,症状上怎么区分?
curl -v 看它连的地址,就能分开这两类。