1. 环境变量是什么
环境变量是操作系统维护的一组 KEY=VALUE 键值对. 每个进程出生时都会从父进程继承一份环境变量的拷贝. 它们是进程间传递配置信息的最基本方式.
|
每个新进程得到的是一份拷贝--子进程改了自己的环境变量后,父进程不受影响. 反过来也一样: 父进程改了,已经存在的子进程看不见.
环境变量就像遗传基因.
每个孩子出生时获得父母基因的复制.
孩子长大后锻炼出肌肉(改了自己的环境变量),父母的基因不会变.
父母后来得了什么病(父进程改了环境变量),已出生的孩子也不会突然获得这个特性.
2. 环境变量 vs Shell 变量
这是一个最常见的困惑. Shell 中有两种变量:
| Shell 变量 | 环境变量 | |
|---|---|---|
| 定义方式 | NAME=value |
export NAME=value |
| 存储位置 | Shell 进程的内存中 | 内核为每个进程维护的环境块 |
| 子进程能否看到 | 不能 | 能(子进程继承拷贝) |
| 查看方式 | set(列出所有 Shell 变量) |
env / printenv |
| 生命周期 | Shell 退出就没了 | Shell 退出就没了(但可以写入配置文件持久化) |
|
最常用的两件事--查看所有环境变量和查看单个环境变量:
|
3. 关键环境变量速览
| 变量 | 值示例 | 含义 |
|---|---|---|
HOME |
/Users/yutong |
用户的家目录. Shell 启动时的默认工作目录 |
PATH |
/usr/local/bin:/usr/bin:/bin |
命令搜索路径,冒号分隔. 最重要的环境变量 |
SHELL |
/bin/zsh |
当前用户的默认登录 Shell |
USER |
yutong |
当前用户名 |
PWD |
/Users/yutong/note/mine |
当前工作目录(Shell 每次 cd 都会更新它) |
OLDPWD |
/Users/yutong |
上一次的 PWD(cd - 回到的目录) |
LANG |
en_US.UTF-8 |
系统的语言和编码 |
EDITOR |
vim 或 code |
默认文本编辑器(git commit 等会用到) |
PS1 |
那一长串提示符模板 | Shell 提示符(仅 Shell 变量,通常不 export) |
4. PATH: 命令查找的 "地图"
这是最重要的环境变量,没有之一. 它决定了 "当敲 ls 时,Shell 去哪找 ls 这个可执行文件".
4.1 命令解析全过程
在 Shell 中输入 git status 并按 Enter 时,Shell 按以下顺序查找 git:
|
|
4.2 PATH 的目录顺序为什么重要
Shell 按 PATH 中的目录顺序查找,找到第一个匹配的就停. 这意味着:
|
这就是为什么安装 Homebrew 后,安装脚本会把 /opt/homebrew/bin 追加到 PATH 最前面--确保 Homebrew 安装的工具优先于系统自带的同名工具.
4.3 PATH 与安全: 不要把 . 放进 PATH
一个经典的安全漏洞:
|
永远不要把 . 或空目录放进 PATH
如果需要执行当前目录下的脚本,显式加上 ./ 前缀: ./myscript.sh. Shell 看到路径中有 / 就不会去 PATH 里找了.
5. export: 让变量被子进程继承
export 做的事很简单: 把这个变量从 Shell 变量提升为环境变量,标记为 "子进程应该继承".
|
哪些变量应该 export?
应该 export: 子进程需要的配置(PATH, HOME, LANG, EDITOR, 数据库连接串...)
不应该 export: 只给当前 Shell 用的(PS1, 历史设置, alias, Shell 选项)
6. 配置文件全景
环境变量只在内存中--Shell 退出就消失. 要让它们每次启动 Shell 时自动生效,需要写进配置文件.
6.1 Zsh 的启动文件加载顺序
不同场景下 zsh 读取不同的文件. 理解它就不需要每次改了配置再 source ~/.zshrc 后猜 "为什么没生效".
| 场景 | 加载顺序 | 放什么 |
|---|---|---|
| 登录 Shell | /etc/zprofile → ~/.zprofile |
登录时执行一次的命令(设置环境变量, 启动 ssh-agent) |
| 交互 Shell | /etc/zshrc → ~/.zshrc |
每次打开终端都要生效的(alias, PS1, 补全配置) |
| 登录 + 交互 | 先 .zprofile,再 .zshrc |
macOS 终端默认走这条 |
| 脚本(非交互) | 什么都不读 | - |
| 始终(任何场景) | /etc/zshenv → ~/.zshenv |
极少数情况(通常避免使用,因为脚本也会读) |
|
6.2 Bash 的启动文件加载顺序
Bash 的逻辑略有不同--登录 Shell 只读 profile,交互非登录只读 bashrc:
| 场景 | Bash 加载 |
|---|---|
| 登录 Shell | /etc/profile → ~/.bash_profile(或 ~/.bash_login 或 ~/.profile,只读第一个) |
| 交互非登录 | /etc/bashrc → ~/.bashrc |
常见的做法: 在 ~/.bash_profile 里写一行 source ~/.bashrc,这样无论哪种启动都最终读到 bashrc.
6.3 实际配置建议
|
改了 ~/.zshrc 后为什么要 source? source 到底做了什么?
source ~/.zshrc(等价于 . ~/.zshrc)不是 "重启 Shell"--它让当前 Shell 进程直接执行文件中的每一行命令. 所以改了 PATH 后 source 一下,当前 Shell 的 PATH 就更新了,不用关窗口重开.
它的本质和 bash script.sh 完全不同:
|
7. alias: 命令的快捷方式
alias 是 Shell 的纯文本替换机制--在命令解析的第一步就完成替换,不做任何语法检查.
|
alias 的局限
alias 是纯文本替换,不能传参数. 如果需要参数,用 Shell 函数:
|
8. 常用配置模式
以下是几个在实际配置中反复出现的模式:
在 PATH 前追加目录
|
根据操作系统做不同配置
|
加载私密配置
|
快速回顾
- 环境变量 = 进程间传递配置的 KEY=VALUE 键值对,子进程继承父进程的拷贝
- Shell 变量只在当前 Shell 内可见; 加
export后子进程才能继承 - PATH = 命令查找的目录列表,冒号分隔,按顺序搜索,找到即停
- 不要把
.放进 PATH--执行当前目录的命令用./command - 登录 Shell 读
~/.zprofile(放环境变量),交互 Shell 读~/.zshrc(放 alias, PS1 等 Shell 专属配置) - macOS 终端默认是登录 Shell--所以
~/.zprofile和~/.zshrc都会被加载 - source 让当前 Shell 逐行执行文件内容--不是重启,是 "就地执行"
- alias 是纯文本替换,不能传参; 需要参数用 Shell 函数
动手练习
- 查看环境变量: 运行
env,找到 HOME, PATH, SHELL, USER 的值 - PATH 搜索: 运行
which git和which -a git,确认 git 在 PATH 的哪个位置. 对比echo $PATH中各目录的顺序 - 环境变量继承: 运行
export MY_TEST="hello from parent"; bash -c 'echo $MY_TEST',验证子进程继承了环境变量 - Shell 变量不继承: 运行
MY_LOCAL="only here"; bash -c 'echo $MY_LOCAL',验证不带 export 的变量子进程看不到 - 阅读配置文件: 阅读
~/.zshrc和~/.zprofile--找出所有 export 的变量和 alias 定义 - 临时 alias: 创建一个临时 alias:
alias today="date +%Y-%m-%d",然后运行today. 关掉终端重开,today还在吗?