Shell環(huán)境變量

關(guān)于Shell

為了能對(duì)shell能夠有整體的認(rèn)識(shí),我們需要先簡單介紹下Linux系統(tǒng) 。

Linux系統(tǒng)

Linux系統(tǒng)主要分四部分:

  1. Linux內(nèi)核
  2. GNU工具
  3. 圖形桌面化環(huán)境
  4. 應(yīng)用軟件

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4ac43fb0199425cbaf1220e34378384~tplv-k3u1fbpfcp-zoom-1.image" alt="Linux系統(tǒng)" style="zoom:50%;" />

Linux內(nèi)核

Linux內(nèi)核主要負(fù)責(zé)以下四種功能:

  • 系統(tǒng)內(nèi)存管理:物理內(nèi)存、虛擬內(nèi)存
  • 軟件程序管理:Linux操作系統(tǒng)將運(yùn)行中的程序稱為進(jìn)程。內(nèi)核控制著Linux系統(tǒng)如何管理運(yùn)行在系統(tǒng)上的所有進(jìn)程。內(nèi)核創(chuàng)建了第一個(gè)進(jìn)程(稱為init進(jìn)程)來啟動(dòng)系統(tǒng)上所有其他進(jìn)程,Linux使用一個(gè)表來管理在系統(tǒng)開機(jī)時(shí)要自動(dòng)啟動(dòng)的進(jìn)程。Linux操作系統(tǒng)的init系統(tǒng)采用了運(yùn)行級(jí)。運(yùn)行級(jí)決定了init進(jìn)程運(yùn)行/etc/inittab文件或 /etc/rcX.d目錄中定義好的某些特定類型的進(jìn)程(X代表運(yùn)行級(jí))。Linux操作系統(tǒng)有5個(gè)啟動(dòng)運(yùn)行級(jí)。每個(gè)啟動(dòng)運(yùn)行級(jí)便是一種啟動(dòng)模式。
  • 硬件設(shè)備管理:內(nèi)核的另一職責(zé)是管理硬件設(shè)備。任何Linux系統(tǒng)需要與之通信的設(shè)備,都需要在內(nèi)核代碼中加入其驅(qū)動(dòng)程序代碼。驅(qū)動(dòng)程序代碼相當(dāng)于應(yīng)用程序和硬件設(shè)備的中間人,允許內(nèi)核與設(shè)備之 間交換數(shù)據(jù)。
  • 文件系統(tǒng)管理:不同于其他一些操作系統(tǒng),Linux內(nèi)核支持通過不同類型的文件系統(tǒng)從硬盤中讀寫數(shù)據(jù)。

GNU

操作系統(tǒng)用以執(zhí)行一些標(biāo)準(zhǔn)功能,比如控制文件和程序的工具。Linus在創(chuàng)建Linux系統(tǒng)內(nèi)核時(shí),沒有可用的系統(tǒng)工具。GNU是由GNU組織(GNUGNU’s Not Unix的縮寫)開發(fā)了一套完整的Unix工具,是開源的,但沒有運(yùn)行它們的內(nèi)核系統(tǒng)。于是將LinusLinux內(nèi)核和GNU操作系統(tǒng)工具整合起來,就產(chǎn)生了一款完整的、功能豐富的免費(fèi)操作系統(tǒng):GNU/Linux系統(tǒng)(為了感謝GNU組織)也稱:Linux系統(tǒng)。

GNU分兩部分,一部分為核心GNU工具(core utilities),由處理文件、操作文本、管理進(jìn)程三部分工具包組成;另一部分便是Shell。

Shell簡介

Shell是一種特殊的交互式工具。它為用戶提供了啟動(dòng)程序、管理文件系統(tǒng)中的文件以及運(yùn)行在Linux系統(tǒng)上的進(jìn)程的途徑。也就是Shell負(fù)責(zé)將命令行中輸入的文本命令,進(jìn)行解釋,并傳遞到內(nèi)核進(jìn)行執(zhí)行的工具,也可稱解釋器。

Shell的核心是命令行提示符。命令行提示符是Shell負(fù)責(zé)交互的部分,它允許你輸入文本命令,然后解釋命令,并在內(nèi)核中執(zhí)行。將多個(gè)shell命令放入文件中作為程序執(zhí)行,這個(gè)文件便被稱為Shell 腳本。

Linux系統(tǒng)上,通常有好幾種Linux shell可用。不同的shell有不同的特性,有些更利于創(chuàng)建腳本,有些則更利于管理進(jìn)程。所有Linux發(fā)行版(完整的Linux系統(tǒng)包)默認(rèn)的shell都是bash shell

bash shellGNU組織開發(fā),被當(dāng)作標(biāo)準(zhǔn)Unix shell——Bourne shell(以創(chuàng)建者的名字命名)的替代品。bash shell的名稱就是針對(duì)Bourne shell的拼寫所玩的一個(gè)文字游戲,稱為Bourne again shell。總結(jié):sh是標(biāo)準(zhǔn),bashsh的替代品。除了bash shellLinux中常見的幾種不同shell有:

  • ash:一種運(yùn)行在內(nèi)存受限環(huán)境中簡單的輕量級(jí)shell,但與bash shell完全兼容。
  • korn:一種與Bourne shell兼容的編程shell,但支持如關(guān)聯(lián)數(shù)組和浮點(diǎn)運(yùn)算等一些高級(jí)的編程特性。
  • tcsh:一種將C語言中的一些元素引入到shell腳本中的shell。
  • zsh:一種結(jié)合了bash、tcshkorn的特性,同時(shí)提供高級(jí)編程特性、共享歷史文件和主題化提示符的高級(jí) shell。

macOS Catalina 版開始,蘋果的Mac系統(tǒng)將使用zsh作為默認(rèn)登錄Shell 和交互式 Shell。具體請(qǐng)看官網(wǎng)。

環(huán)境變量

這一部分將基于bash shell展開陳述。

bash shell中使用環(huán)境變量在內(nèi)存中存儲(chǔ)有關(guān)shell會(huì)話和工作環(huán)境的數(shù)據(jù),以便程序或shell中運(yùn)行的腳本能夠訪問到它們。

bash shell中的環(huán)境變量主要有兩種:全局變量與局部變量。查看系統(tǒng)中所有全局變量,可以使用envprintenv命令;要顯示個(gè)別環(huán)境變量的值,可以使用printenv命令,但是不能用env命令。

#查看shell個(gè)別全局變量
printenv HOME
#查看shell個(gè)別全局變量:通過echo 以變量的形式輸出
echo $HOME

系統(tǒng)的環(huán)境都是大寫,定義屬于用戶自己局部變量時(shí)統(tǒng)一使用小寫,避免沖突。

定義局部變量

定義形式如下:

variable=Hello
echo $variable #輸出:Hello

重要:變量名、等號(hào)和值之間沒有空格。如果在賦值表達(dá)式中加上了空格, bash shell就會(huì)把值當(dāng)成一個(gè)單獨(dú)的命令,變量值有空格需要使用引號(hào)。

variable="Hello word"
echo $variable #輸出:Hello word

定義全局變量

在設(shè)定全局環(huán)境變量的進(jìn)程所創(chuàng)建的子進(jìn)程中,該全局變量都是可見的;創(chuàng)建全局環(huán)境變量的方法是先創(chuàng)建一個(gè)局部環(huán)境變量,然后再把它導(dǎo)出到全局環(huán)境中;父shell定義的全局變量,子shell的修改不會(huì)影響父shell的值。示例如下:

variable="global variable ~~~~"
export variable
#開啟子shell
bash
#子shell 輸出一下
echo $variable #global variable ~~~~
#子shell修改
variable="子shell修改"
#子shell 輸出一下
echo $variable #輸出:子shell修改
#導(dǎo)出
export variable
#退出子shell
exit
#父shell輸出
echo $variable #子shell的修改不會(huì)影響父shell:global variable ~~~~

刪除全局變量

使用如下命令:

#刪除
unset variable

需要注意的是在子shell中是無法刪除父shell創(chuàng)建的全局變量。

默認(rèn)全局變量

默認(rèn)情況下,bash shell會(huì)用一些特定的環(huán)境變量來定義系統(tǒng)環(huán)境。這些變量在你的Linux系統(tǒng)上都已經(jīng)設(shè)置好了,只管放心使用。bash shell源自當(dāng)初的Unix Bourne shell,因此也保留了Unix Bourne shell里定義的那些環(huán)境變量。
列舉幾個(gè)比較常見的環(huán)境變量:

  • CDPATH:冒號(hào)分隔的目錄列表,作為cd命令的搜索路徑
  • HOME:當(dāng)前用戶的主目錄
  • PATHshell查找命令的目錄列表,由冒號(hào)分隔
  • BASH:當(dāng)前shell實(shí)例的全路徑名
  • PWD:當(dāng)前工作目錄

設(shè)置PATH變量

當(dāng)我們?cè)?code>shell命令行界面中輸入一個(gè)外部命令時(shí),shell必須搜索系統(tǒng)來找到對(duì)應(yīng)的程序。PATH環(huán)境變量定義了用于進(jìn)行命令和程序查找的目錄:

#輸出下
echo $PATH
#結(jié)果
/Users/*/.rvm/gems/ruby-2.3.0/bin:
/Users/*/.rvm/gems/ruby-2.3.0@global/bin:
/Users/*/.rvm/rubies/ruby-2.3.0/bin:
/Users/*/Desktop/development/flutter/bin:
/usr/local/bin:
/usr/bin:
/bin:
/usr/sbin:
/sbin:
/Users/*/.rvm/bin

輸出的結(jié)果中顯示了有10個(gè)可供shell用來查找命令和程序的路徑。PATH中的目錄使用冒號(hào)分隔。這些路徑下分別都存放了不同的命令和程序,舉個(gè)/bin的示例:

/bin目錄下的程序與命令

如果命令或者程序的路徑?jīng)]有包括在PATH變量中,則不使用絕對(duì)路徑的情況下,shell是沒法找到該程序的。

問題:應(yīng)用程序放置可執(zhí)行文件的目錄常常不在PATH環(huán)境變量所包含的目錄中。

解決:是保證PATH環(huán)境變量包含了所有存放應(yīng)用程序的目錄??梢园研碌乃阉髂夸浱砑拥浆F(xiàn)有的PATH環(huán)境變量中,無需從頭定義。PATH中各個(gè)目錄之間是用冒號(hào)分隔的,我們只需要引用原來的PATH值,然后再給這個(gè)字符串添加新目錄就行了。

舉例終端啟用flutter命令:

#查看path
echo $PATH
#結(jié)果不包含:/Users/*/Desktop/development/flutter/bin:
#通過終端啟用
PATH=$PATH:~/Desktop/development/flutter/bin
#再次查看
echo $PATH
#結(jié)果包含:/Users/*/Desktop/development/flutter/bin:
#執(zhí)行flutter
flutter -v
#不再提示找不到命令。

值得注意的是:對(duì)PATH變量的修改只能持續(xù)到退出或重啟終端系統(tǒng)。這種效果并不能一直持續(xù)。如何讓這種效果持續(xù)?

為了解決這個(gè)問題,我們需要了解一些關(guān)于定位環(huán)境變量的知識(shí)。

在我們登入Linux系統(tǒng)啟動(dòng)一個(gè)bash shell時(shí),默認(rèn)情況下bash會(huì)在幾個(gè)文件中查找命令。這些文件叫作啟動(dòng)文件或環(huán)境文件。bash檢查的啟動(dòng)文件取決于你啟動(dòng)bash shell的方式。啟動(dòng)bash shell3種方式:

  • 登錄時(shí)作為默認(rèn)登錄shell
  • 作為非登錄shell的交互式shell
  • 作為運(yùn)行腳本的非交互shell

默認(rèn)登錄shell

當(dāng)我們登錄Linux系統(tǒng)時(shí),bash shell會(huì)作為登錄shell啟動(dòng)。登錄shell會(huì)從5個(gè)不同的啟動(dòng)文件里讀取命令:

  1. /etc/profile
#/etc/profile啟動(dòng)文件內(nèi)容

# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi
  1. $HOME/.bash_profile
  2. $HOME/.bashrc
#macOS系統(tǒng)中.bashrc啟動(dòng)文件內(nèi)容,非登錄的交互式shell會(huì)以此為啟動(dòng)文件的。
export PATH="$PATH:$HOME/.rvm/bin"
  1. $HOME/.bash_login
  2. $HOME/.profile

/etc/profile文件是系統(tǒng)上默認(rèn)的bash shell的主啟動(dòng)文件。系統(tǒng)上的每個(gè)用戶登錄時(shí)都會(huì)執(zhí)行這個(gè)啟動(dòng)文件。另外4個(gè)啟動(dòng)文件是針對(duì)用戶的,提供一個(gè)用戶專屬的啟動(dòng)文件來定義該用戶所用到的環(huán)境變量,可根據(jù)個(gè)人需求定制,且都是隱藏文件。它們位于用戶的HOME目錄下,所以每個(gè)用戶都可以編輯這些文件并添加自己的環(huán)境變 量,這些環(huán)境變量會(huì)在每次啟動(dòng)bash shell會(huì)話時(shí)生效。其中25我們?cè)?code>macOS中是比較熟悉的。

shell會(huì)按照下列順序,運(yùn)行第一個(gè)被找到的文件,余下的則被忽略(不會(huì)重復(fù)):
$HOME/.bash_profile=>$HOME/.bash_login=>$HOME/.profile 注:$HOME和波浪號(hào)~作用一樣,都代表用戶目錄。正是因?yàn)榇艘?guī)則的存在,我們有些時(shí)候只需要在$HOME/.profile中配置我們的PATH即可。

非登錄的交互式shell

不是登錄時(shí)啟動(dòng)的shell稱為交互式shell。比如:在命令行提示符下敲入bash時(shí)啟動(dòng)。
當(dāng)bash是作為交互式shell啟動(dòng),則不會(huì)訪問/etc/profile文件,只會(huì)檢查用戶目錄$HOME下的.bashrc文件。

運(yùn)行腳本的非交互shell

非交互shell,系統(tǒng)執(zhí)行shell腳本時(shí)會(huì)使用。不同的地方在于它沒有命令行提示符。但是當(dāng)我們?cè)谙到y(tǒng)上運(yùn)行腳本時(shí),可能希望運(yùn)行一些特定啟動(dòng)的命令。為了處理這種情況,bash shell提供了BASH_ENV環(huán)境變量。當(dāng)shell啟動(dòng)一個(gè)非交互式shell進(jìn)程時(shí),它會(huì)檢查這個(gè)環(huán)境變量來查看要執(zhí)行的啟動(dòng)文件。

macOS系統(tǒng)下運(yùn)行echo $BASH_ENV查看,這個(gè)環(huán)境變量并未被設(shè)置。如果BASH_ENV變量沒有設(shè)置,shell腳本如何獲得它們的環(huán)境變量呢?

  • shell可以繼承父shell到處的環(huán)境變量;但需要注意的是父shell中設(shè)置但卻未export的變量,屬于局部變量,子shell是無法獲取的。也就是說執(zhí)行腳本時(shí),采用bash命令開啟子shell便可以解決這個(gè)問題。
  • 不啟動(dòng)子shell的腳本,變量已經(jīng)存在于當(dāng)前shell中。

環(huán)境變量持久化

Linux在大多數(shù)發(fā)行版中,存儲(chǔ)個(gè)人用戶永久性bash shell變量的地方是$HOME/.bashrc文件。這一 點(diǎn)適用于所有類型的shell進(jìn)程。但如果設(shè)置了BASH_ENV變量,那么除非BASH_ENV指向的是 $HOME/.bashrc,否則應(yīng)該將非交互式shell的用戶變量放在別的地方。

macOS系統(tǒng)中,存儲(chǔ)個(gè)人用戶永久性bash shell變量的地方,便是對(duì)應(yīng)的環(huán)境文件:~/.profile~/.bash_profile、~/.bashrc(交互式shell生效)。其中~/.profile~/.bash_profile任意一個(gè)都可以定義我們的永久性bash shell變量。

#在`~/.profile`文件中定義
export LOVE="全局可用的環(huán)境變量love"
PEACE="局部環(huán)境變量,子shell不可用"
#在`~/.bash_profile`文件中定義
export SHARE="全局可用的環(huán)境變量share"
QI="局部環(huán)境變量,子shell不可用"
#終端shell,查看全局變量
env
#輸出
LOVE=全局可用的環(huán)境變量love
SHARE=全局可用的環(huán)境變量share
#終端shell,查看永久局部變量
echo $PEACE
echo $QI
#輸出
局部環(huán)境變量,子shell不可用
#開啟非登陸交互式shell
bash
#子shell,查看可用全局變量
env
#輸出
LOVE=全局可用的環(huán)境變量love
SHARE=全局可用的環(huán)境變量share
#子shell,查看可用的父Shell的局部變量
echo $PEACE
echo $QI
#輸出為空

關(guān)于~/.bashrc,作為非登錄式交互shell的啟動(dòng)文件,僅對(duì)非登錄式交互shell生效:

#在`~/.bashrc`配置
export CHILD="子shell可用"
#重新打開終端,輸入
env
#or
echo $CHILD
#都輸出:空
 
#開啟子shell(交互式shell)
bash
#查看子shell的全局變量
env 
#or
echo $CHILD
#輸出
CHILD=子shell可用
子shell可用
#再次開啟子shell
bash
#輸入
env 
#or
echo $CHILD
#輸出
CHILD=子shell可用
子shell可用
#如果`~/.bashrc`文件中配置
CHILD="子shell可用"
#輸入
env
#輸出:空
#輸入
echo $CHILD
#輸出
子shell可用
#再次開啟子shell,`CHILD`變量將不再生效

數(shù)組變量

數(shù)組變量:環(huán)境變量設(shè)置多個(gè)值,放置在括號(hào)中,且值與值之間用空格隔開。

#定義數(shù)組變量
ARRAY_VAR=(one two three)
#輸出數(shù)組變量
echo $ARRAY_VAR
#不會(huì)全部輸出,只會(huì)輸出數(shù)組變量中第一個(gè)元素
one

要引用數(shù)組變量中的某個(gè)元素,就必須用代表它在數(shù)組中位置的數(shù)值索引值。索引值要用方括號(hào)括起來:

echo ${ARRAY_VAR[2]}
#輸出
three
#顯示整個(gè)數(shù)組
echo ${ARRAY_VAR[*]}

可以用unset命令刪除數(shù)組中的某個(gè)值,但是要小心。比如:

#刪除索引為1的元素
unset ARRAY_VAR[1]
#輸出整個(gè)數(shù)組
echo ${ARRAY_VAR[*]}
#刪除成功
one three
#輸出數(shù)組中索引為1的元素
echo ${ARRAY_VAR[1]}
#結(jié)果為:空

#輸出索引為2的元素
echo ${ARRAY_VAR[2]}
#結(jié)果
3

參考資料:
Linux命令行與shell腳本編程大全

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容