Linux 環(huán)境變量能提升 shell 使用體驗
很多程序和腳本都通過環(huán)境變量獲取系統(tǒng)信息、存儲臨時數(shù)據(jù)和配置信息
更多精彩
- 更多技術(shù)博客,請移步 IT人才終生實(shí)訓(xùn)與職業(yè)進(jìn)階平臺 - 實(shí)訓(xùn)在線
6.1 什么是環(huán)境變量
- shell 中用來存儲有關(guān) shell 會話和工作環(huán)境的變量,被叫做 環(huán)境變量( Environment Variable )
- 環(huán)境變量 被存儲在內(nèi)存中,方便程序或在 shell 中運(yùn)行的腳本能輕松訪問
- shell 中的環(huán)境變量分為兩類
- 全局環(huán)境變量
- 局部環(huán)境變量
6.1.1 全局環(huán)境變量
-
全局環(huán)境變量 之所以被叫做 全局 ,是因為其對于 父 shell 和所有生成的 子 shell 會話都是可見的
- 但 局部環(huán)境變量 只對創(chuàng)建它們的 shell 可見
- Linux 內(nèi)置了很多默認(rèn)的 全局環(huán)境變量 ,會在用戶登錄 shell 時依次加載,這種 全局環(huán)境變量 被叫做 系統(tǒng)環(huán)境變量
- 系統(tǒng)環(huán)境變量 基本上都是使用全大寫字母,用來區(qū)別用戶自行創(chuàng)建的環(huán)境變量
6.1.1.1 env 命令和 printenv 命令查看全局環(huán)境變量
- 使用
env命令和printenv命令可以查看當(dāng)前系統(tǒng)的全局環(huán)境變量,如下圖
-
printenv命令和env命令的區(qū)別在于,printenv命令可以查看單個變量,而env命令不行,如下圖
6.1.1.2 echo 命令查看全局環(huán)境變量
- 使用
echo命令也可以查看單個變量的值,但語法稍微復(fù)雜一點(diǎn),需要在變量名稱前加一個美元符號 ,如下圖
6.1.1.3 全局環(huán)境變量的全局觀
- 前面說過,全局環(huán)境變量 之所以被叫做 全局 ,是因為在 父 shell 和 子 shell 中都可以訪問,如下圖
6.1.2 局部環(huán)境變量
- 局部環(huán)境變量 只能在創(chuàng)建它們的會話中使用
- Linux 同樣內(nèi)置了很多默認(rèn)的 局部環(huán)境變量
6.1.2.1 set 命令查看局部環(huán)境變量
- Linux 沒有內(nèi)置專門用來查看 局部環(huán)境變量 的命令
- 使用
set命令只是可以在執(zhí)行結(jié)果中順便看到 局部環(huán)境變量,如下圖- 除了 局部環(huán)境變量 ,還會顯示 全局環(huán)境變量 ,以及用戶自定義環(huán)境變量
-
顯示結(jié)果是按字母排序的
6.2 設(shè)置用戶定義變量
6.2.1 設(shè)置局部用戶定義變量
- 定義局部用戶變量不需要使用任何命令,直接定義一個變量名,并通過等號賦值即可,如下圖
- 定義變量并賦值后,可以使用
echo $variable命令來顯示變量值 -
如果變量值存在空格,就不能直接賦值,需要將變量值用雙引號包裹
- 定義變量并賦值后,可以使用
6.2.1.1 設(shè)置變量的語法規(guī)范
- 需要注意的是 ,大部分編程語言中,在變量名、等號和值之間添加空格,被認(rèn)為是一種松散且美觀的編碼方式
- 但是在 shell 中,這一點(diǎn)行不通,shell 中采用的是盡量緊湊的編碼方式,變量名、等號和值之間不能有空格
- 但是的但是,這一個要點(diǎn),在 shell 中也沒有被徹底貫徹,就比如第 5 章中介紹到的
coproc 命令,如果要在使用coproc命令的同時自定義協(xié)程名稱,則需要使用coproc coprocName { command; }命令,花括號中的空格是必不可少的,否則是語法錯誤 - 我認(rèn)為,這可能跟編寫這些語法的大神們自身的風(fēng)格有關(guān),誰知道呢?
6.2.1.2 局部變量父子間無法通信
- 在 父 shell 中定義的局部變量,無法在 子 shell 中訪問,如下圖
-
echo命令在 子 shell 中嘗試訪問在 父 shell 中定義的變量,得到的執(zhí)行結(jié)果是一行空白
-
- 相同的,在 子 shell 中定義的局部變量,也無法在 父 shell 中訪問,如下圖
6.2.2 export 命令設(shè)置全局環(huán)境變量
- 使用
export命令并不能直接定義 全局環(huán)境變量 - 需要先定義一個 局部環(huán)境變量 ,再使用
export命令將這個變量提升為 全局環(huán)境變量 ,如下圖- 在 父 shell 中定義的局部變量,使用
export命令提升后,在 子 shell 中也能夠順利訪問 -
需要注意的是 ,使用
export variable命令時,變量名前面不需要在美元符號
- 在 父 shell 中定義的局部變量,使用
6.2.2.1 自定義的全局變量在子 shell 中是只讀的
- 由 父 shell 定義的 全局環(huán)境變量 ,任何 子 shell 都可以訪問,但無法修改,如下圖
- 在 父 shell 中定義變量并提升后,在 子 shell 中可以訪問
- 在 子 shell 中對該變量進(jìn)行重新賦值并提升后,退出 子 shell ,在 父 shell 中
6.3 unset 命令刪除環(huán)境變量
- 使用
unset命令可以刪除定義的環(huán)境變量,如下圖- 使用
unset variable命令后,就無法使用echo $variable訪問到對應(yīng)變量,說明變量已經(jīng)被刪除
- 使用
6.3.1 子 shell 無法刪除父 shell 定義的全局變量
- 在 6.2.2.1 節(jié)中提到,父 shell 中定義的全局變量,對于子 shell 是只讀的 ,所以 子 shell 自然也無法刪除 父 shell 定義的全局變量,如下圖
- 在 子 shell 中刪除由 父 shell 定義的變量后,只是當(dāng)前 子 shell 無法再訪問該變量
- 但退出當(dāng)前 子 shell 后,父 shell 依舊能夠順利訪問到該變量,說明該變量對于 子 shell 是只讀的
6.4 默認(rèn)的 shell 環(huán)境變量
- 默認(rèn)情況下,Linux 會提供一些特定的環(huán)境變量用于定義 系統(tǒng)環(huán)境變量
- bash shell 的部分 系統(tǒng)環(huán)境變量 ,繼承自 Unix Bourne shell ,例如 HOME 、PATH
- bash shell 自身也定義了很多 系統(tǒng)環(huán)境變量 ,例如 HISTSIZE 、BASH
6.5 設(shè)置 PATH 環(huán)境變量
-
PATH 是一個很關(guān)鍵的 系統(tǒng)環(huán)境變量 ,它定義了 用于進(jìn)行命令和程序查找的目錄 ,如下圖
- 在第 5 章介紹過 shell 命令分為 內(nèi)建命令 和 外部命令
- 我們之所以能直接使用 外部命令 ,就是因為在 PATH 中將存放 外部命令 的目錄進(jìn)行了指定
- PATH 中的不同目錄,使用分號進(jìn)行分隔
- 如果某個命令或程序的位置不包含在 PATH 指定的目錄中,則無法進(jìn)行全局調(diào)用
6.5.1 為 PATH 添加自定義目錄
- PATH 中的目錄默認(rèn)情況下都是系統(tǒng)預(yù)置的,一般就包括用于存放 外部命令 的 /bin 、/usr/bin 、/sbin 、/usr/sbin 目錄
- 如果想要讓其他程序的命令也能夠全局訪問,例如安裝 JDK 環(huán)境后一般都需要配置環(huán)境變量
- 只需要使用
PATH=$PATH:customCommandDirectory即可- 這句命令的意思就是為 PATH 變量追加一個自定義的命令目錄,相當(dāng)于其他語言中的
+=操作
- 這句命令的意思就是為 PATH 變量追加一個自定義的命令目錄,相當(dāng)于其他語言中的
- 不過這種直接在 shell 會話中執(zhí)行的添加操作,是一次性的 ,如果當(dāng)前會話退出,或系統(tǒng)重啟,這一次的配置操作也會隨之失效
6.6 定位系統(tǒng)環(huán)境變量
- 上一節(jié)介紹到為 PATH 添加自定義目錄,但實(shí)現(xiàn)的效果是一次性的
- 要了解如何讓自定義的環(huán)境變量持久化存在與系統(tǒng)中,需要先了解 shell 的三種啟動方式
- 登錄式 shell :登錄時作為默認(rèn) shell 啟動
-
交互式 shell :在當(dāng)前 shell 會話中通過
bash命令或zsh命令啟動的各種類型 子 shell - 非交互式 shell :在當(dāng)前 shell 會話中通過腳本運(yùn)行的 shell
6.6.1 登錄式 shell
- 登錄 Linux 時,啟動的 shell 就叫做 登錄式 shell
- 當(dāng) 登錄式 shell 啟動時,首先會嘗試讀取以下 5 個不同的啟動文件
6.6.1.1 /etc/profile 文件的作用
- /etc/profile 文件作為 shell 默認(rèn)的主啟動文件,主要是在系統(tǒng)啟動時,做一些默認(rèn)操作
- 同時會在文件末尾掃描指定的目錄,用于加載可能存在的自定義啟動文件
- 例如在 CentOS 的 /etc/profile 中,末尾有如下一段代碼
- 這段代碼的大概意思就是會掃描 /etc/profile.d 目錄下的所有 .sh 文件
- 所以一般推薦將一些自定義的全局環(huán)境變量、啟動文件放置在該目錄下
- 如果偷懶直接在 /etc/profile 文件中進(jìn)行自定義修改,在系統(tǒng)升級后,可能該文件會被重置,那么之前做的自定義修改也就失效了
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then
. "$i"
else
. "$i" >/dev/null 2>&1
fi
fi
done
- 不過在 macOS 的 /etc/profile 中,末尾沒有上述這段代碼,而是下面這段代碼
- 在文件末尾有一個判斷,大概意思是說:如果當(dāng)前的 shell 類型是 bash shell ,則加載 bashrc 配置
- 由于我的 macOS 環(huán)境下使用的是 zsh shell ,所以輸出
${BASH-no}的結(jié)果是 no ,那么 bashrc 配置自然也不會加載
6.6.1.2 存放在 $HOME 目錄下的用戶啟動文件
-
$HOME 目錄其實(shí)指的就是用戶根目錄,這是一個默認(rèn)的 系統(tǒng)環(huán)境變量 ,如下圖
- 該目錄下的啟動文件作用都一樣,提供一個用戶專屬的啟動文件用于定義該用戶會用到的環(huán)境變量
- 一般情況下,Linux 系統(tǒng)中都不會內(nèi)置全部的這四個文件,通常只存在一到兩個
-
在 macOS 中的查詢結(jié)果,就只有兩個,如下圖
-
在 CentOS 中的查詢結(jié)果,也只有兩個,如下圖
- 會被 shell 直接掃描的文件只有 .bash_profile 、.bash_login 、.profile
- 需要注意的是 ,掃描過程中,并不是三個文件都一定會被掃描到
- 掃描的規(guī)則是,運(yùn)行第一個被找到的文件,其他的直接忽略
- 這可能就是在上述兩個系統(tǒng)環(huán)境中都沒有找到 .bash_login 和 .profile 文件的原因吧,我猜的
-
.bashrc 文件不會被直接掃描的原因是因為該文件通常會被其他文件執(zhí)行
- 在 CentOS 中被 .bash_profile 執(zhí)行
- 在 macOS 中被 /etc/profile 執(zhí)行
6.6.2 交互式 shell 進(jìn)程
- 在 登錄式 shell 中通過對應(yīng)類型的 shell 命令啟動的 shell ,就叫做 交互式 shell
-
交互式 shell 啟動時,不會訪問 /etc/profile 文件,會直接訪問 $HOME/.bashrc 文件
- 前提是這個 shell 的類型是 bash shell ,如果是其他 shell ,則訪問對應(yīng)類型 shell 的啟動文件
- 例如前文中說到的 zsh shell ,就會訪問 $HOME/.zshrc 文件
-
.bashrc 的作用如下
- 執(zhí)行 /etc 目錄下通用的 bashrc 文件
- 為用戶提供一個 自定義命令別名( alias ) ,以及 私有腳本函數(shù) 的位置
6.6.3 非交互式 shell
- 在當(dāng)前 shell 會話中通過腳本運(yùn)行的 shell 就叫做 非交互式 shell
-
非交互式 shell 在執(zhí)行時,不會直接去訪問任何的啟動文件,而是直接繼承當(dāng)前執(zhí)行環(huán)境的 系統(tǒng)環(huán)境變量 以及 全局環(huán)境變量
- 與 子 shell 和 父 shell 之間局部變量無法通信一樣
- 非交互式 shell 也無法訪問 父 shell 中沒有被提升的局部變量
6.6.4 環(huán)境變量持久化
- 在前文的 6.6.1.1 節(jié)中就有提到,將自定義的全局環(huán)境變量放置在 /etc/profile 中不是一個好主意
- 而是應(yīng)該放置在 /etc/profile.d 中,該目錄是 CentOS 專門用來存放用戶自定義 .sh 啟動文件的位置
- $HOME/.bashrc 是存儲自定義 bash shell 變量的位置,如果要存放 zsh shell ,則需要使用 $HOME/.zshrc
6.7 數(shù)組變量
- 數(shù)組變量并不實(shí)用,就好像所有的編程語言都應(yīng)該擁有數(shù)組一樣,在 shell 中,它只是被支持,但不常用
6.7.1 數(shù)組變量的基本語法
- 要定義一個數(shù)組變量,只需要在等號后面將多個用空格分隔的值,使用括號包裹即可,如下圖
- 下圖的運(yùn)行環(huán)境是 CentOS
-
可以看到,同樣一句話,使用雙引號包裹,就是字符串,使用括號包裹就是數(shù)組,而且直接輸出的結(jié)果不一樣
- 實(shí)測發(fā)現(xiàn) macOS 輸出的結(jié)果和 CentOS 不一樣,畢竟 macOS 是基于 Unix 開發(fā),而不是 Linux ,如下圖
- 在 macOS 中直接輸出數(shù)組,也可以得到和字符串一樣的輸出結(jié)果
-
同時使用索引訪問數(shù)組成員時,第 0 位是空,第 1 位才有值
6.7.2 通過索引訪問數(shù)組成員
- 在上一節(jié)的第一個圖中,可以看到直接訪問數(shù)組的結(jié)果是默認(rèn)輸出第一個值
- 那么如果要訪問數(shù)組的其他成員,則需要使用索引,如下圖
- 索引從 0 開始,而且不支持負(fù)值
- 可以看到和上一節(jié)第二個圖中 macOS 的查詢效果差別很大
-
花括號和變量名直接也需要是緊湊的,不能有空格
6.7.3 星號顯示數(shù)組所有成員
- 要實(shí)現(xiàn)和字符串一樣的輸出效果,可以在指定索引的位置使用一個星號,如下圖
-
這一操作 macOS 也支持
-
6.7.4 修改數(shù)組指定索引的值
- 要修改數(shù)組指定索引的值,只需要直接為數(shù)組指定索引的成員賦值即可,如下圖
- 一個索引只能只能一個值,不能指定多個值
-
但可以通過雙引號將多個值標(biāo)記為普通字符串,就可以實(shí)現(xiàn)類似效果
6.7.5 unset 命令刪除數(shù)組指定索引的值
- 使用
unset命令可以刪除數(shù)組指定索引的值,但這個刪除操作并不會更新數(shù)組的索引排序,如下圖- 可以看到,使用
unset mySigns[1]后,之前位于該索引的值就不存在了 - 但使用
echo ${mySigns[1]}嘗試輸出后,得到的是一個空值,而不是之前位于索引 2 位置的值 -
這一點(diǎn)就和其他編程語言不一樣,例如 Java 和 Javascript ,在這些語言中,如果索引 1 的值被刪掉,數(shù)組的索引排序?qū)?,再次訪問索引 1 時,會得到之前位于索引 2 的值
- 可以看到,使用
6.8 小結(jié)
- Linux 中的環(huán)境變量分為 全局環(huán)境變量 和 局部環(huán)境變量
- 全局環(huán)境變量 可以在父子級之間訪問,但父級定義的 全局環(huán)境變量 對于子級來說是只讀的
- 局部環(huán)境變量 無法在父子級之間訪問,只能在定義該變量的會話中訪問
- PATH 是一個很關(guān)鍵的 全局環(huán)境變量 ,也叫 系統(tǒng)環(huán)境變量 ,其為 shell 執(zhí)行各種命令指定了搜索目錄
- PATH 支持自定義修改,可以將各種不是系統(tǒng)自帶的命令加入其中,從而實(shí)現(xiàn)命令的全局訪問
- shell 的啟動方式分為三種,分別是 登錄式 shell 、交互式 shell 、非交互 shell
- /etc/profile 是 shell 的主啟動文件,在 登錄式 shell 啟動時會被訪問
- .bashrc 是用于存放用戶自定義變量的持久化文件,在 登錄式 shell 和 交互式 shell 啟動時會被訪問
- 環(huán)境變量數(shù)組 是一個即不好用,也不常用的特性
























