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