那天同事在我的電腦上給我演示一個操作的步驟,使用了一個env
命令,好吧,我承認我文盲,我不知道這個命令是干嘛的!!!正是由於我的無知,反倒激起了學習探索的欲望,一定要把這個env
學個透徹。經過幾個下班後的空閒時間,終於搞清楚了這“坨”東西,記錄成文,和大家分享,如果對大家有所幫助,或者能夠掃除大家的一些盲點,我也就勝感欣慰。
這就是你眼中的Shell,當你在Linux中,打開Terminal的時候,出現了一個黑屏,又或一個白屏的窗口的,哦,這就是我們眼中的Shell,這種理解也許對,也許不對。
當我們打開Terminal的時候,其實運行了一個默認的Shell解釋器,我們一般都是/bin/bash
,當我們在終端中輸入各種命令時,都是由這個/bin/bash
來進行解釋的;也就是說Shell是運行在Terminal上的一個程序。
明白了Shell以後,我們經常會在當前的Shell中運行一個Shell腳本,當你運行這個Shell腳本的時候,你是否知道這背後在干些什麼麼?這又說到下一個話題:父Shell與子Shell。
如果不明白父Shell與子Shell的關系,那麼這篇文章重點要講的Shell中的變量,也就說不清了。
當我們啟動Terminal的時候,會運行一個Shell進程,暫且叫做Shell進程A;當我們在這個Terminal中運行Shell腳本的時候,進程A會fork出一個新進程,從而啟動另一個Shell解釋器(這由腳本中第一行指定的,例如:#!/bin/bash),fork出來的這個新進程暫且叫做進程B。此時,進程A和進程B就是父、子進程的關系;進程B是一個子Shell,而進程A則是父Shell。一旦子Shell中的腳本執行完畢,此子Shell隨即結束,返回到父進程。這就是父Shell和子Shell。
我想我應該把父Shell和子Shell說清了,明白了這層關系以後,我們繼續總結。
首先,你要意識到一點,就是你登陸Linux系統,打開Terminal,使用Shell的時候,系統在背後讀取了一大“坨”的配置文件,這些配置文件決定了你的Shell中的變量。所以,在具體總結Shell中的變量時,我們還需要來看看讀了哪些配置文件,以及讀取這些配置文件的順序是什麼。
/etc/profile
:該文件為系統的每個用戶設置環境信息,當用戶第一次登錄系統時,該文件會被執行。 並從/etc/profile.d目錄的配置文件中讀取shell的設置;~/.bash_profile
或者~/.bash_login
或者~/.profile
:系統會依次尋找這三個文件,這些是針對當前用戶的配置,但是需要注意的是,這三個文件一般不會同時存在,即使同時存在,系統按照這個順序找到了一個以後,就不會再去讀剩下的了;~/.bashrc
:該文件包含了專屬於當前登錄用戶的bash shell的bash信息,當登錄以及每次打開新的shell時,該文件都會被讀取;/etc/bashrc
:為每一個運行bash shell的用戶執行此文件;當bash shell被打開時,該文件被讀取。這些腳本配置文件決定了你的系統變量和環境變量等。如果你感興趣,你可以去看看這些腳本的源碼,你就會知道它們到底是怎麼調用的了。當然了,如果我們需要定義一些我們經常用到的變量,比如配置JDK的時候,你可能還要編輯它們。
先不帶子Shell玩,把單個Shell進程中的變量捋清了,再把子Shell加進來一起總結。
在Shell中有以下三種變量:
export
將用戶變量轉為環境變量;比如以下這些就是內部變量:
變量名
描述
$#
命令行參數個數
$0
當前程序的名稱
$?
前一個命令或函數的返回碼
$$
當前程序的PID
以下這些是我們常用的一些環境變量,一般我們使用env
命令查看當前用戶的環境變量。
變量名
描述
PATH
表示Shell將到哪些目錄中尋找命令或程序
SHELL
當前用戶的Shell類型
HOME
當前用戶主目錄
PS1
基本提示符
用戶變量(本地變量)那就比較隨性了,你可以自己隨意定義,比如:
> str='Hello World'> echo $str
我們使用set
命令來顯示當前Shell中定義的用戶變量,當然了set
命令也會輸出環境變量。
上面說的這些你明白了麼?我們繼續。
當我們在Shell裡面再運行一個Shell腳本的時候,這個時候會fork出一個新的Shell進程,此時就會有父、子Shell兩個進程了。有了父、子關系,那麼父進程中的Shell變量會遺傳到子進程中麼???同時,子進程中的Shell變量會返回到父進程麼???這些都是我們需要關注的。先來看一個例子:
父Shell定義一個變量:
> str='Hello World'
然後在父Shell中運行以下腳本:
#!/bin/bash# 輸出父Shell中定義的str
echo $str
# 輸出環境變量
echo $HOME
echo $PATH
你會發現,運行以後,$str輸出空,但是$HOME和$PATH卻可以很完美的輸出。這也說明,我們在一個Shell中定義的用戶變量,只能被當前Shell所使用,別人是無法訪問到的,即使是子Shell也不例外;而父進程的環境變量是可以在子進程中被訪問。但是有的時候,我們有這樣的需求:
在子進程中訪問父進程的用戶變量(本地變量),這該怎麼辦???
當我們遇到這樣的需求時,我就不得不說一下export
命令了。
export
命令可以將用戶變量設置為環境變量,從而可以在子Shell進程中訪問該變量。這正好也好和export
的中文含義相符。對於之前的例子,我們可以在父Shell中輸入一下命令:
> export str
再次執行腳本時,就可以輸出用戶變量str的值了;這就是export
的作用。但是,我們在父Shell中輸入export str
以後,當我們關閉父Shell以後,該環境變量將失效,如果想打開Shell就能立刻設置export
,我們可以按照我們的需要,將export str
寫到上面總結的那一“坨”啟動配置文件中,這樣就不會因為關閉了父Shell而導致export失效。
現在我們可以在子進程中訪問父進程的變量了,你是否想過,在子Shell中修改父Shell的變量是否會影響父Shell中該變量的值呢?不妨做個測試。
#!/bin/bash# 修改父Shell傳遞過來的變量str的值
$str='http://www.jellythink.com'
echo $str
運行腳本,發現在父Shell中,str的值並沒有發生改變。其實,這又關系到Linux中關於進程的另一個知識點。當父進程中fork一個子進程時,子進程會拷貝父進程的相關變量,此時,子進程就會擁有和父進程同名同值的變量,雖然同名同值,但是卻只是父進程的一個副本,對於副本的修改都和父進程無關。關於Linux進程知識,可以參考這篇文章。
而如果我們在子Shell中定義一個變量,反過來在父Shell中是否可以訪問呢?實踐告訴我們,這樣是行不通的,你不可以在父Shell中訪問子Shell中定義的變量。如果想在父Shell中訪問子Shell中定義的變量,可以借助一個臨時文件,將局部變量寫入臨時文件,父Shell讀取這個文件,從而達到訪問子Shell中定義的變量的目的。
這篇文章雖然總結的多而雜,但是每一部分都是息息相關、環環相扣的,對於大家整體理解Linux Shell有非常大的幫助,也有利於大家學習Linux Shell。文章有點長,需要一點耐心去把它從頭到尾好好的讀完。為了學習,是需要一點耐心的,你說呢?這一篇就總結到這裡了,下一篇再見。
果凍想-一個原創技術文章分享網站。
2015年10月21日 於呼和浩特。
這裡對文中涉及到的命令以及一些相關命令進行總結一下。
命令
描述
set
顯示本地定義的Shell變量
unset
清除環境變量,例如:unset str
export
設置一個新的環境變量
env
顯示當前用戶所有環境變量