聊聊Linux Shell

系列文章首發平臺爲[果凍想個人博客][1]。果凍想,是一個原創技術文章分享網站。在這裏果凍會分享他的技術心得,技術得失,技術人生。我在[果凍想][1]等待你,也希望你能和我分享你的技術得與失,期待。

果凍想-一個原創技術文章分享網站

爲什麼寫這篇文章

那天同事在我的電腦上給我演示一個操作的步驟,使用了一個env命令,好吧,我承認我文盲,我不知道這個命令是幹嘛的!!!正是由於我的無知,反倒激起了學習探索的慾望,一定要把這個env學個透徹。經過幾個下班後的空閒時間,終於搞清楚了這“坨”東西,記錄成文,和大家分享,如果對大家有所幫助,或者能夠掃除大家的一些盲點,我也就勝感欣慰。

對Shell最簡單的認識

這就是你眼中的Shell,當你在Linux中,打開Terminal的時候,出現了一個黑屏,又或一個白屏的窗口的,哦,這就是我們眼中的Shell,這種理解也許對,也許不對。

這是Linux Shell

當我們打開Terminal的時候,其實運行了一個默認的Shell解釋器,我們一般都是/bin/bash,當我們在終端中輸入各種命令時,都是由這個/bin/bash來進行解釋的;也就是說Shell是運行在Terminal上的一個程序。

明白了Shell以後,我們經常會在當前的Shell中運行一個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說清了,明白了這層關係以後,我們繼續總結。

Shell初始化讀的一些配置文件

首先,你要意識到一點,就是你登陸Linux系統,打開Terminal,使用Shell的時候,系統在背後讀取了一大“坨”的配置文件,這些配置文件決定了你的Shell中的變量。所以,在具體總結Shell中的變量時,我們還需要來看看讀了哪些配置文件,以及讀取這些配置文件的順序是什麼。

  1. /etc/profile:該文件爲系統的每個用戶設置環境信息,當用戶第一次登錄系統時,該文件會被執行。 並從/etc/profile.d目錄的配置文件中讀取shell的設置;
  2. ~/.bash_profile或者~/.bash_login或者~/.profile:系統會依次尋找這三個文件,這些是針對當前用戶的配置,但是需要注意的是,這三個文件一般不會同時存在,即使同時存在,系統按照這個順序找到了一個以後,就不會再去讀剩下的了;
  3. ~/.bashrc:該文件包含了專屬於當前登錄用戶的bash shell的bash信息,當登錄以及每次打開新的shell時,該文件都會被讀取;
  4. /etc/bashrc:爲每一個運行bash shell的用戶執行此文件;當bash shell被打開時,該文件被讀取。

這些腳本配置文件決定了你的系統變量和環境變量等。如果你感興趣,你可以去看看這些腳本的源碼,你就會知道它們到底是怎麼調用的了。當然了,如果我們需要定義一些我們經常用到的變量,比如配置JDK的時候,你可能還要編輯它們。

Shell中的變量

先不帶子Shell玩,把單個Shell進程中的變量捋清了,再把子Shell加進來一起總結。

在Shell中有以下三種變量:

  • 內部變量;系統定義,不能修改;
  • 環境變量;系統定義,可以修改,可以利用export將用戶變量轉爲環境變量;
  • 用戶變量;用戶定義,可以定義,玩壞了都沒事。

比如以下這些就是內部變量:

變量名 描述
$# 命令行參數個數
$0 當前程序的名稱
$? 前一個命令或函數的返回碼
$$ 當前程序的PID

以下這些是我們常用的一些環境變量,一般我們使用env命令查看當前用戶的環境變量。

變量名 描述
PATH 表示Shell將到哪些目錄中尋找命令或程序
SHELL 當前用戶的Shell類型
HOME 當前用戶主目錄
PS1 基本提示符

用戶變量(本地變量)那就比較隨性了,你可以自己隨意定義,比如:

> str='Hello World'
> echo $str

我們使用set命令來顯示當前Shell中定義的用戶變量,當然了set命令也會輸出環境變量。

上面說的這些你明白了麼?我們繼續。

帶上子Shell一起玩

當我們在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命令

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一個子進程時,子進程會拷貝父進程的相關變量,此時,子進程就會擁有和父進程同名同值的變量,雖然同名同值,但是卻只是父進程的一個副本,對於副本的修改都和父進程無關。

而如果我們在子Shell中定義一個變量,反過來在父Shell中是否可以訪問呢?實踐告訴我們,這樣是行不通的,你不可以在父Shell中訪問子Shell中定義的變量。如果想在父Shell中訪問子Shell中定義的變量,可以藉助一個臨時文件,將局部變量寫入臨時文件,父Shell讀取這個文件,從而達到訪問子Shell中定義的變量的目的。

總結

這篇文章雖然總結的多而雜,但是每一部分都是息息相關、環環相扣的,對於大家整體理解Linux Shell有非常大的幫助,也有利於大家學習Linux Shell。文章有點長,需要一點耐心去把它從頭到尾好好的讀完。爲了學習,是需要一點耐心的,你說呢?這一篇就總結到這裏了,下一篇再見。

果凍想-一個原創技術文章分享網站。

2015年10月21日 於呼和浩特。

附錄

這裏對文中涉及到的命令以及一些相關命令進行總結一下。

命令 描述
set 顯示本地定義的Shell變量
unset 清除環境變量,例如:unset str
export 設置一個新的環境變量
env 顯示當前用戶所有環境變量

如果你覺的文章還不錯,可以關注果凍想微信公衆號,定期推送技術文章:
果凍想微信公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章