echo $PATH命令找不到时第一个要看的。各目录用冒号分隔,从左往右找。
约 1350 字大约 5 分钟
2026-05-14
"明明装了这个命令,为什么提示 command not found?""为什么 cron 里跑不出我终端里的效果?"——这两个高频问题的答案,几乎都落在环境变量和 PATH 上。环境变量是进程之间传递配置的标准方式,理解它能省掉大量"玄学"调试。
理解环境变量是什么、如何被子进程继承,PATH 如何决定命令的查找,以及 export 和 source 的区别。
echo $PATH命令找不到时第一个要看的。各目录用冒号分隔,从左往右找。
which -a python-a 列出 PATH 里所有同名命令,看真正执行的是哪个。
env列出当前环境里所有会被子进程继承的变量。
export KEY=value设置变量并标记为“导出”,之后启动的子进程才能继承。
Shell 里的变量分两种:
NAME=shuo # 普通变量,只在当前 Shell 可见
export NAME=shuo # 环境变量,会被这个 Shell 启动的子进程继承区别在于"子进程能不能看到"。你运行一个脚本、启动一个服务,都是开了个子进程;只有被 export 过的变量才会传进去。这就是为什么程序的配置经常用环境变量传递——它是跨进程传配置的天然通道。
继承是单向的:父进程传给子进程,子进程改了不会影响父进程。所以脚本里 export 一个变量,脚本跑完回到终端,那个变量并不存在。
输入 ls 时,Shell 并不知道 ls 在哪。它会按 PATH 里列出的目录,从左到右逐个找名为 ls 的可执行文件,找到第一个就用:
echo $PATH
# /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin由此能解释两个常见问题:
PATH 里。要么用绝对路径 /opt/app/bin/tool,要么把目录加进 PATH。PATH 里靠前的目录有同名命令。用 which -a python 看全部命中,顺序靠前的胜出。往 PATH 加目录的标准写法,把新目录放前面或后面取决于优先级:
export PATH="$HOME/.local/bin:$PATH" # 自己的目录优先注意一定要带上 :$PATH,否则你会把整个 PATH 覆盖成只剩一个目录,瞬间几乎所有命令都用不了。
这两个经常一起出现,但解决的是不同问题:
| 操作 | 作用 | 典型场景 |
|---|---|---|
export VAR=x | 把变量标记为"传给子进程" | 让脚本 / 程序读到配置 |
source file | 在当前 Shell 里执行文件内容 | 改完 ~/.bashrc 想立刻生效 |
source ~/.bashrc(等价于 . ~/.bashrc)和 bash ~/.bashrc 的关键差别:前者在当前 Shell 执行,设的变量留得下来;后者开子进程执行,跑完变量就没了。所以改完配置文件要 source,不能 bash。
环境变量的设置散落在多个文件,改错地方就不生效:
~/.bashrc —— 每开一个交互 Shell 都读(非登录)~/.bash_profile / ~/.profile —— 登录时读(SSH、su -)/etc/environment、/etc/profile.d/*.sh —— 全系统级别改完之后,要么 source 对应文件,要么重开终端。还不生效,就 echo $VAR 确认到底有没有设进去,再 which 确认 PATH 命中。
别把密钥写进会进版本库的文件
数据库密码、API key 这类敏感值,不要写进会被 git 跟踪的脚本或 .bashrc。用单独的、加进 .gitignore 的 .env 文件,或系统的密钥管理。history 也会记录你手敲的 export SECRET=...,分享终端记录前记得检查。
| 误区 | 更稳妥的做法 |
|---|---|
PATH=/new/dir 直接赋值 | 一定写成 PATH="/new/dir:$PATH",否则覆盖掉原有 PATH |
改完 .bashrc 用 bash .bashrc 让它生效 | 用 source ~/.bashrc,子进程执行的改动留不下来 |
| 以为脚本能读到终端里设的普通变量 | 只有 export 过的变量才被子进程继承 |
密钥直接 export 在 .bashrc 里 | 放进 gitignore 的独立文件,注意 history 也会留痕 |
版权归属:Shuo Liu