Shell 启动脚本:理解 bash、zsh 的启动流程
如果你是一个经常使用 shell 的用户,几乎肯定会在主目录下有一个 .bash_profile
或 .bashrc
脚本,通常包含各种调整,比如设置环境变量(将某个目录添加到 $PATH
)、告诉 shell 做聪明的事情(如 set -o noclobber
)以及为命令添加各种别名(如 alias please=sudo
)。
(如果你真的很有条理,你会把所有点文件都放在某个仓库中,这样你就可以在所有工作的机器上保持设置同步。)
无论如何,我怀疑很少有人知道 .bash_profile
和 .bashrc
这样的文件实际上什么时候被执行。当我刚开始时,我只是按照别人的建议把东西放在 .bashrc
中,然后当它不工作时,就放到 .bash_profile
中。我可以在这里停下来,只描述 bash 的启动过程(尽管它很愚蠢),但有一个复杂的情况是,我在几年前切换到了 zsh(并且没有回头),但偶尔会在没有安装 zsh 的机器上使用 bash。
Shell 启动脚本的复杂性
为了优雅地处理这种情况,我需要能够在它们自己的文件中指定特定于 bash 或 zsh 的内容,然后在通用启动文件中指定任何符合 POSIX 标准的 shell(如别名和环境变量)都能理解的内容。
我对这个问题的解决方案是定义一些新的点文件文件夹,每个 shell 一个(.bash/
、.zsh/
和 .sh/
),还有一个用于 shell 无关文件(.shell/
):
1 | .bash/ |
不同类型的 Shell
"But!" 你说,"这些不同的文件在这里做什么?" 啊,我很高兴你问了。有两种类型的 shell:
- [非]交互式 shell(你向它们输入 / shell 脚本)
- [非]登录 shell(首次登录时运行的 shell / 子 shell)
所有 shell 都会首先运行 env
,然后登录 shell 会运行 login
,然后交互式 shell 会运行 interactive
。完成后,登录 shell 会运行 logout
。
在哪里放置内容
这完全取决于它什么时候需要运行。
- 如果它正在设置 / 修改环境变量,它应该放在
login
中 - 如果它是别名或终端特定的环境变量(例如,
GREP_COLOR
),它应该放在interactive
中 - 在我的
.shell/env
文件中,我设置了umask
,还定义了一些有用的函数来修改冒号分隔的路径环境变量(如$PATH
)
即使你不采用我方案中的其他任何东西,我也建议你看看我的函数在做什么,这与像 export PATH=$PATH:/path/to/dir
这样的东西不同。
这种特定模式太常见了,如果你考虑 $PATH
(或任何你的变量,如 $LD_LIBRARY_PATH
)没有设置的情况,它会非常危险。然后,值将是 :/path/to/dir
,这通常意味着 /path/to/dir
和当前目录,这通常既是意外行为又是安全问题。
使用我的实现(见 .shell/env_functions
),你可以从任何冒号分隔的环境变量中追加、前置和删除目录,当追加或前置时,你保证该目录只会在该变量中出现一次。
Shell 启动流程详解
Bash 启动流程
Bash 的启动流程相对复杂,因为它有多种模式:
- 登录 shell:当 bash 以
-l
参数启动或作为登录 shell 启动时 - 交互式 shell:当 bash 以交互模式启动时
- 非交互式 shell:当 bash 执行脚本时
- 远程 shell:当 bash 检测到通过 ssh 或 rsh 启动时
Zsh 启动流程
Zsh 的启动流程更加简洁和一致:
- 全局配置文件:
/etc/zshenv
、/etc/zprofile
、/etc/zshrc
、/etc/zlogout
- 用户配置文件:
~/.zshenv
、~/.zprofile
、~/.zshrc
、~/.zlogout
启动文件执行顺序
1 | 登录 shell: |
实际应用建议
1. 环境变量管理
避免使用 export PATH=$PATH:/new/path
这种模式,因为它可能导致重复和空路径问题。相反,使用更安全的方法:
1 | # 安全的路径添加函数 |
2. 条件加载
根据 shell 类型和交互性条件性地加载配置:
1 | # 只在交互式 shell 中加载 |
3. 跨 Shell 兼容性
为了在不同 shell 之间保持兼容性,使用 POSIX 兼容的语法:
1 | # 使用 POSIX 兼容的语法 |
总结
理解 shell 启动脚本的执行顺序对于正确配置你的环境至关重要。通过采用模块化的方法,你可以:
- 保持配置的整洁:将不同类型的配置分离到不同的文件中
- 提高可维护性:每个文件都有明确的职责
- 增强可移植性:在不同机器和 shell 之间轻松迁移配置
- 避免常见陷阱:如路径重复、环境变量污染等问题
记住,shell 启动脚本的执行顺序可能因操作系统、shell 版本和编译选项而异。最好的方法是测试你自己的系统,并根据需要调整配置。