Shell 在 MacOS 及 Linux 中的文件讀取順序
MacOS 與 Linux 中 zsh 的加載順序
Interactive login | Interactive non-login | Script | |
---|---|---|---|
/etc/zshenv |
A | A | A |
~/.zshenv |
B | B | B |
/etc/zprofile |
C | ||
~/.zprofile |
D | ||
/etc/zshrc |
E | C | |
~/.zshrc |
F | D | |
/etc/zlogin |
G | ||
~/.zlogin |
H | ||
~/.zlogout |
I | ||
/etc/zlogout |
J |
如文章首圖與上表所示, zsh
/ bash
/ sh
都有針對(duì)于 login
/ nologin
及 interactive
, non-interactive
的不同讀取順序. 主要分為以下四種情形
-
interactive - login
: 登錄遠(yuǎn)程主機(jī)(e.gssh
) -
interactive - non-login
: 打開一個(gè)新 terminal 或一個(gè)新建 terminal 窗口 -
non-interactive - non-login
: 執(zhí)行一個(gè)腳本所在的sub-shell
就處于這種狀態(tài)(這種狀態(tài)下不能進(jìn)行交互, 這種狀態(tài)也只存在于腳本執(zhí)行的d短暫時(shí)間內(nèi)) -
non-interactive - login
: 這種情況很少見, 比如這種情況echo 'echo $-; shopt login_shell' | ssh localhost
特別值得強(qiáng)調(diào)的是, 在 MacOS 中, 如果我們打開一個(gè) terminal 或新開一個(gè)tab時(shí), zsh 會(huì)將其作為
login
對(duì)待. 當(dāng)然, 在運(yùn)行腳本所創(chuàng)建的sub-shell
中,
其狀態(tài)都是non-interactive
Mac 下的 /etc/zprofile
對(duì) PATH 做的奇怪事情
在 Mac 下有 /etc/zprofile
文件, 在這個(gè)文件中有這樣一段命令
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
由首圖及上表可知, zsh 會(huì)在 ~/.zshenv
之后加載 /etc/zprofile
, 進(jìn)而執(zhí)行 /usr/libexec/path_helper
這個(gè)腳本, 根據(jù)這個(gè) blog 的描述, 這個(gè)腳本工具會(huì)將變量 $PATH
的順序重排, 同時(shí)讀取 /etc/paths
與 /etc/manpaths
中的 path
. 順著這個(gè)思路, 如果我們?cè)?zshenv
中定義了 $PATH
, 那么精心配好的順序無(wú)疑會(huì)被打亂.
由此我們可以得出一個(gè)結(jié)論: 變量 $PATH
的定義必須必須放在 /etc/zprofile
之后
MacVim 中讀取到的 $PATH
由于 MacOS
的特殊性(如果我們打開一個(gè) terminal
或新開一個(gè) tab
時(shí), zsh 會(huì)將其作為 login
對(duì)待), 在開啟 MacVim 時(shí), MacVim 讀取到的文件順序與 login - non-interactive
是相同的
也就是說(shuō), MacVim 不會(huì)讀取 ~/.zshrc
, 會(huì)讀取 ~/.zprofile
, 那么如果我們需要在 MacVim 中獲得正確的 $PATH
, 就必須將 $PATH
及一些其他基本變量的設(shè)置
放在 ~/.zprofile
中
打造一個(gè)兼容各個(gè)系統(tǒng)的 zsh
配置
我使用的是 zsh
, 我覺(jué)得非常順手, 各種功能強(qiáng)大, 兼容主流平臺(tái), 因此我的所有工作環(huán)境都會(huì)使用 zsh
. 由于平時(shí)會(huì)使用 MacOS
與 Linux
,
而且想要將自己的一些個(gè)性化配置在不同環(huán)境下通用, 那么就需要考慮下如何在 MacOS
, MacVim
, ssh remote
, Linux Desktop
下有統(tǒng)一的加載行為.
經(jīng)過(guò)考慮, 我目前的方案是:
-
創(chuàng)建一個(gè)
init.zsh
腳本, 在其中有各種變量的定義, 包括$PATH
. 在腳本開始進(jìn)行加載的判斷(防止同一次啟動(dòng)中重復(fù)加載次腳本)if [ -z "$_INIT_ZSH_LOADED" ]; then _INIT_ZSH_LOADED=1 else return fi
在
.zprofile
的開頭source init.zsh
在
.zshrc
的開頭source init.zsh
在
.zlogin
中使用想要顯示的交互信息(比如neofetch
信息)
這里附上我的 shell 配置倉(cāng)庫(kù), 供各位參考
https://github.com/HanleyLee/dotsh