Linux 命令行與 shell 腳本編程大全 6 使用 Linux 環境變量

Linux 環境變量能提升 shell 使用體驗
很多程序和腳本都通過環境變量獲取系統信息、存儲臨時數據和配置信息

更多精彩

6.1 什麼是環境變量

  1. shell 中用來存儲有關 shell 會話和工作環境的變量,被叫做 環境變量( Environment Variable )
  2. 環境變量 被存儲在內存中,方便程序或在 shell 中運行的腳本能輕鬆訪問
  3. shell 中的環境變量分爲兩類
    • 全局環境變量
    • 局部環境變量

6.1.1 全局環境變量

  1. 全局環境變量 之所以被叫做 全局 ,是因爲其對於 父 shell 和所有生成的 子 shell 會話都是可見的
    • 局部環境變量 只對創建它們的 shell 可見
  2. Linux 內置了很多默認的 全局環境變量 ,會在用戶登錄 shell 時依次加載,這種 全局環境變量 被叫做 系統環境變量
  3. 系統環境變量 基本上都是使用全大寫字母,用來區別用戶自行創建的環境變量

6.1.1.1 env 命令和 printenv 命令查看全局環境變量

  1. 使用 env 命令和 printenv 命令可以查看當前系統的全局環境變量,如下圖

  2. printenv 命令和 env 命令的區別在於,printenv 命令可以查看單個變量,而 env 命令不行,如下圖

6.1.1.2 echo 命令查看全局環境變量

  1. 使用 echo 命令也可以查看單個變量的值,但語法稍微複雜一點,需要在變量名稱前加一個美元符號 ,如下圖

6.1.1.3 全局環境變量的全局觀

  1. 前面說過,全局環境變量 之所以被叫做 全局 ,是因爲在 父 shell子 shell 中都可以訪問,如下圖

6.1.2 局部環境變量

  1. 局部環境變量 只能在創建它們的會話中使用
  2. Linux 同樣內置了很多默認的 局部環境變量

6.1.2.1 set 命令查看局部環境變量

  1. Linux 沒有內置專門用來查看 局部環境變量 的命令
  2. 使用 set 命令只是可以在執行結果中順便看到 局部環境變量,如下圖
    • 除了 局部環境變量 ,還會顯示 全局環境變量 ,以及用戶自定義環境變量
    • 顯示結果是按字母排序的

6.2 設置用戶定義變量

6.2.1 設置局部用戶定義變量

  1. 定義局部用戶變量不需要使用任何命令,直接定義一個變量名,並通過等號賦值即可,如下圖
    • 定義變量並賦值後,可以使用 echo $variable 命令來顯示變量值
    • 如果變量值存在空格,就不能直接賦值,需要將變量值用雙引號包裹

6.2.1.1 設置變量的語法規範

  1. 需要注意的是 ,大部分編程語言中,在變量名、等號和值之間添加空格,被認爲是一種鬆散且美觀的編碼方式
  2. 但是在 shell 中,這一點行不通,shell 中採用的是儘量緊湊的編碼方式,變量名、等號和值之間不能有空格
  3. 但是的但是,這一個要點,在 shell 中也沒有被徹底貫徹,就比如第 5 章中介紹到的 coproc 命令 ,如果要在使用 coproc 命令的同時自定義協程名稱,則需要使用 coproc coprocName { command; } 命令,花括號中的空格是必不可少的,否則是語法錯誤
  4. 我認爲,這可能跟編寫這些語法的大神們自身的風格有關,誰知道呢?

6.2.1.2 局部變量父子間無法通信

  1. 父 shell 中定義的局部變量,無法在 子 shell 中訪問,如下圖
    • echo 命令在 子 shell 中嘗試訪問在 父 shell 中定義的變量,得到的執行結果是一行空白
  2. 相同的,在 子 shell 中定義的局部變量,也無法在 父 shell 中訪問,如下圖

6.2.2 export 命令設置全局環境變量

  1. 使用 export 命令並不能直接定義 全局環境變量
  2. 需要先定義一個 局部環境變量 ,再使用 export 命令將這個變量提升爲 全局環境變量 ,如下圖
    • 父 shell 中定義的局部變量,使用 export 命令提升後,在 子 shell 中也能夠順利訪問
    • 需要注意的是 ,使用 export variable 命令時,變量名前面不需要在美元符號

6.2.2.1 自定義的全局變量在子 shell 中是隻讀的

  1. 父 shell 定義的 全局環境變量 ,任何 子 shell 都可以訪問,但無法修改,如下圖
    • 父 shell 中定義變量並提升後,在 子 shell 中可以訪問
    • 子 shell 中對該變量進行重新賦值並提升後,退出 子 shell ,在 父 shell

6.3 unset 命令刪除環境變量

  1. 使用 unset 命令可以刪除定義的環境變量,如下圖
    • 使用 unset variable 命令後,就無法使用 echo $variable 訪問到對應變量,說明變量已經被刪除

6.3.1 子 shell 無法刪除父 shell 定義的全局變量

  1. 在 6.2.2.1 節中提到,父 shell 中定義的全局變量,對於子 shell 是隻讀的 ,所以 子 shell 自然也無法刪除 父 shell 定義的全局變量,如下圖
    • 子 shell 中刪除由 父 shell 定義的變量後,只是當前 子 shell 無法再訪問該變量
    • 但退出當前 子 shell 後,父 shell 依舊能夠順利訪問到該變量,說明該變量對於 子 shell 是隻讀的

6.4 默認的 shell 環境變量

  1. 默認情況下,Linux 會提供一些特定的環境變量用於定義 系統環境變量
  2. bash shell 的部分 系統環境變量 ,繼承自 Unix Bourne shell ,例如 HOMEPATH
  3. bash shell 自身也定義了很多 系統環境變量 ,例如 HISTSIZEBASH

6.5 設置 PATH 環境變量

  1. PATH 是一個很關鍵的 系統環境變量 ,它定義了 用於進行命令和程序查找的目錄 ,如下圖
    • 在第 5 章介紹過 shell 命令分爲 內建命令外部命令
    • 我們之所以能直接使用 外部命令 ,就是因爲在 PATH 中將存放 外部命令 的目錄進行了指定
  2. PATH 中的不同目錄,使用分號進行分隔
  3. 如果某個命令或程序的位置不包含在 PATH 指定的目錄中,則無法進行全局調用

6.5.1 爲 PATH 添加自定義目錄

  1. PATH 中的目錄默認情況下都是系統預置的,一般就包括用於存放 外部命令/bin/usr/bin/sbin/usr/sbin 目錄
  2. 如果想要讓其他程序的命令也能夠全局訪問,例如安裝 JDK 環境後一般都需要配置環境變量
  3. 只需要使用 PATH=$PATH:customCommandDirectory 即可
    • 這句命令的意思就是爲 PATH 變量追加一個自定義的命令目錄,相當於其他語言中的 += 操作
  4. 不過這種直接在 shell 會話中執行的添加操作,是一次性的 ,如果當前會話退出,或系統重啓,這一次的配置操作也會隨之失效

6.6 定位系統環境變量

  1. 上一節介紹到爲 PATH 添加自定義目錄,但實現的效果是一次性的
  2. 要了解如何讓自定義的環境變量持久化存在與系統中,需要先了解 shell 的三種啓動方式
    • 登錄式 shell :登錄時作爲默認 shell 啓動
    • 交互式 shell :在當前 shell 會話中通過 bash 命令或 zsh 命令啓動的各種類型 子 shell
    • 非交互式 shell :在當前 shell 會話中通過腳本運行的 shell

6.6.1 登錄式 shell

  1. 登錄 Linux 時,啓動的 shell 就叫做 登錄式 shell
  2. 登錄式 shell 啓動時,首先會嘗試讀取以下 5 個不同的啓動文件

6.6.1.1 /etc/profile 文件的作用

  1. /etc/profile 文件作爲 shell 默認的主啓動文件,主要是在系統啓動時,做一些默認操作
  2. 同時會在文件末尾掃描指定的目錄,用於加載可能存在的自定義啓動文件
  3. 例如在 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
  1. 不過在 macOS 的 /etc/profile 中,末尾沒有上述這段代碼,而是下面這段代碼
    • 在文件末尾有一個判斷,大概意思是說:如果當前的 shell 類型是 bash shell ,則加載 bashrc 配置
    • 由於我的 macOS 環境下使用的是 zsh shell ,所以輸出 ${BASH-no} 的結果是 no ,那麼 bashrc 配置自然也不會加載

6.6.1.2 存放在 $HOME 目錄下的用戶啓動文件

  1. $HOME 目錄其實指的就是用戶根目錄,這是一個默認的 系統環境變量 ,如下圖
  2. 該目錄下的啓動文件作用都一樣,提供一個用戶專屬的啓動文件用於定義該用戶會用到的環境變量
  3. 一般情況下,Linux 系統中都不會內置全部的這四個文件,通常只存在一到兩個
  4. 在 macOS 中的查詢結果,就只有兩個,如下圖
  5. 在 CentOS 中的查詢結果,也只有兩個,如下圖
  6. 會被 shell 直接掃描的文件只有 .bash_profile.bash_login.profile
    • 需要注意的是 ,掃描過程中,並不是三個文件都一定會被掃描到
    • 掃描的規則是,運行第一個被找到的文件,其他的直接忽略
    • 這可能就是在上述兩個系統環境中都沒有找到 .bash_login.profile 文件的原因吧,我猜的
  7. .bashrc 文件不會被直接掃描的原因是因爲該文件通常會被其他文件執行
    • 在 CentOS 中被 .bash_profile 執行
    • 在 macOS 中被 /etc/profile 執行

6.6.2 交互式 shell 進程

  1. 登錄式 shell 中通過對應類型的 shell 命令啓動的 shell ,就叫做 交互式 shell
  2. 交互式 shell 啓動時,不會訪問 /etc/profile 文件,會直接訪問 $HOME/.bashrc 文件
    • 前提是這個 shell 的類型是 bash shell ,如果是其他 shell ,則訪問對應類型 shell 的啓動文件
    • 例如前文中說到的 zsh shell ,就會訪問 $HOME/.zshrc 文件
  3. .bashrc 的作用如下
    1. 執行 /etc 目錄下通用的 bashrc 文件
    2. 爲用戶提供一個 自定義命令別名( alias ) ,以及 私有腳本函數 的位置

6.6.3 非交互式 shell

  1. 在當前 shell 會話中通過腳本運行的 shell 就叫做 非交互式 shell
  2. 非交互式 shell 在執行時,不會直接去訪問任何的啓動文件,而是直接繼承當前執行環境的 系統環境變量 以及 全局環境變量
    • 子 shell父 shell 之間局部變量無法通信一樣
    • 非交互式 shell 也無法訪問 父 shell 中沒有被提升的局部變量

6.6.4 環境變量持久化

  1. 在前文的 6.6.1.1 節中就有提到,將自定義的全局環境變量放置在 /etc/profile 中不是一個好主意
  2. 而是應該放置在 /etc/profile.d 中,該目錄是 CentOS 專門用來存放用戶自定義 .sh 啓動文件的位置
  3. $HOME/.bashrc 是存儲自定義 bash shell 變量的位置,如果要存放 zsh shell ,則需要使用 $HOME/.zshrc

6.7 數組變量

  1. 數組變量並不實用,就好像所有的編程語言都應該擁有數組一樣,在 shell 中,它只是被支持,但不常用

6.7.1 數組變量的基本語法

  1. 要定義一個數組變量,只需要在等號後面將多個用空格分隔的值,使用括號包裹即可,如下圖
    • 下圖的運行環境是 CentOS
    • 可以看到,同樣一句話,使用雙引號包裹,就是字符串,使用括號包裹就是數組,而且直接輸出的結果不一樣
  2. 實測發現 macOS 輸出的結果和 CentOS 不一樣,畢竟 macOS 是基於 Unix 開發,而不是 Linux ,如下圖
    • 在 macOS 中直接輸出數組,也可以得到和字符串一樣的輸出結果
    • 同時使用索引訪問數組成員時,第 0 位是空,第 1 位纔有值

6.7.2 通過索引訪問數組成員

  1. 在上一節的第一個圖中,可以看到直接訪問數組的結果是默認輸出第一個值
  2. 那麼如果要訪問數組的其他成員,則需要使用索引,如下圖
    • 索引從 0 開始,而且不支持負值
    • 可以看到和上一節第二個圖中 macOS 的查詢效果差別很大
    • 花括號和變量名直接也需要是緊湊的,不能有空格

6.7.3 星號顯示數組所有成員

  1. 要實現和字符串一樣的輸出效果,可以在指定索引的位置使用一個星號,如下圖
    • 這一操作 macOS 也支持

6.7.4 修改數組指定索引的值

  1. 要修改數組指定索引的值,只需要直接爲數組指定索引的成員賦值即可,如下圖
    • 一個索引只能只能一個值,不能指定多個值
    • 但可以通過雙引號將多個值標記爲普通字符串,就可以實現類似效果

6.7.5 unset 命令刪除數組指定索引的值

  1. 使用 unset 命令可以刪除數組指定索引的值,但這個刪除操作並不會更新數組的索引排序,如下圖
    • 可以看到,使用 unset mySigns[1] 後,之前位於該索引的值就不存在了
    • 但使用 echo ${mySigns[1]} 嘗試輸出後,得到的是一個空值,而不是之前位於索引 2 位置的值
    • 這一點就和其他編程語言不一樣,例如 Java 和 Javascript ,在這些語言中,如果索引 1 的值被刪掉,數組的索引排序將會更新,再次訪問索引 1 時,會得到之前位於索引 2 的值

6.8 小結

  1. Linux 中的環境變量分爲 全局環境變量局部環境變量
  2. 全局環境變量 可以在父子級之間訪問,但父級定義的 全局環境變量 對於子級來說是隻讀的
  3. 局部環境變量 無法在父子級之間訪問,只能在定義該變量的會話中訪問
  4. PATH 是一個很關鍵的 全局環境變量 ,也叫 系統環境變量 ,其爲 shell 執行各種命令指定了搜索目錄
  5. PATH 支持自定義修改,可以將各種不是系統自帶的命令加入其中,從而實現命令的全局訪問
  6. shell 的啓動方式分爲三種,分別是 登錄式 shell交互式 shell非交互 shell
  7. /etc/profile 是 shell 的主啓動文件,在 登錄式 shell 啓動時會被訪問
  8. .bashrc 是用於存放用戶自定義變量的持久化文件,在 登錄式 shell交互式 shell 啓動時會被訪問
  9. 環境變量數組 是一個即不好用,也不常用的特性
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章