4. bash啟動腳本

啟動腳本是bash啟動時自動執行的腳本。用戶可以把一些環境變數的設置和aliasumask設置放在啟動腳本中,這樣每次啟動Shell時這些設置都自動生效。思考一下,bash在執行啟動腳本時是以fork子Shell方式執行的還是以source方式執行的?

啟動bash的方法不同,執行啟動腳本的步驟也不相同,具體可分為以下幾種情況。

4.1. 作為交互登錄Shell啟動,或者使用--login參數啟動

交互Shell是指用戶在提示符下輸命令的Shell而非執行腳本的Shell,登錄Shell就是在輸入用戶名和密碼登錄後得到的Shell,比如從字元終端登錄或者用telnet/ssh從遠程登錄,但是從圖形界面的窗口管理器登錄之後會顯示桌面而不會產生登錄Shell(也不會執行啟動腳本),在圖形界面下打開終端窗口得到的Shell也不是登錄Shell。

這樣啟動bash會自動執行以下腳本:

  1. 首先執行/etc/profile,系統中每個用戶登錄時都要執行這個腳本,如果係統管理員希望某個設置對所有用戶都生效,可以寫在這個腳本裡

  2. 然後依次查找當前用戶主目錄的~/.bash_profile~/.bash_login~/.profile三個檔案,找到第一個存在並且可讀的檔案來執行,如果希望某個設置只對當前用戶生效,可以寫在這個腳本裡,由於這個腳本在/etc/profile之後執行,/etc/profile設置的一些環境變數的值在這個腳本中可以修改,也就是說,當前用戶的設置可以覆蓋(Override)系統中全局的設置。~/.profile這個啟動腳本是sh規定的,bash規定首先查找以~/.bash_開頭的啟動腳本,如果沒有則執行~/.profile,是為了和sh保持一致。

  3. 順便一提,在退出登錄時會執行~/.bash_logout腳本(如果它存在的話)。

4.2. 以交互非登錄Shell啟動

比如在圖形界面下開一個終端窗口,或者在登錄Shell提示符下再輸入bash命令,就得到一個交互非登錄的Shell,這種Shell在啟動時自動執行~/.bashrc腳本。

為了使登錄Shell也能自動執行~/.bashrc,通常在~/.bash_profile中調用~/.bashrc

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

這幾行的意思是如果~/.bashrc檔案存在則source它。多數Linux發行版在創建帳戶時會自動創建~/.bash_profile~/.bashrc腳本,~/.bash_profile中通常都有上面這幾行。所以,如果要在啟動腳本中做某些設置,使它在圖形終端窗口和字元終端的Shell中都起作用,最好就是在~/.bashrc中設置。

下面做一個實驗,在~/.bashrc檔案末尾添加一行(如果這個檔案不存在就創建它):

export PATH=$PATH:/home/akaedu

然後關掉終端窗口重新打開,或者從字元終端logout之後重新登錄,現在主目錄下的程序應該可以直接輸程序名運行而不必輸入路徑了,例如:

~$ a.out

就可以了,而不必

~$ ./a.out

為什麼登錄Shell和非登錄Shell的啟動腳本要區分開呢?最初的設計是這樣考慮的,如果從字元終端或者遠程登錄,那麼登錄Shell是該用戶的所有其它進程的父進程,也是其它子Shell的父進程,所以環境變數在登錄Shell的啟動腳本裡設置一次就可以自動帶到其它非登錄Shell裡,而Shell的本地變數、函數、alias等設置沒有辦法帶到子Shell裡,需要每次啟動非登錄Shell時設置一遍,所以就需要有非登錄Shell的啟動腳本,所以一般來說在~/.bash_profile裡設置環境變數,在~/.bashrc裡設置本地變數、函數、alias等。如果你的Linux帶有圖形系統則不能這樣設置,由於從圖形界面的窗口管理器登錄並不會產生登錄Shell,所以環境變數也應該在~/.bashrc裡設置。

4.3. 非交互啟動

為執行腳本而fork出來的子Shell是非交互Shell,啟動時執行的腳本檔案由環境變數BASH_ENV定義,相當於自動執行以下命令:

if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi

如果環境變數BASH_ENV的值不是空字元串,則把它的值當作啟動腳本的檔案名,source這個腳本。

4.4. 以sh命令啟動

如果以sh命令啟動bashbash將模擬sh的行為,以~/.bash_開頭的那些啟動腳本就不認了。所以,如果作為交互登錄Shell啟動,或者使用--login參數啟動,則依次執行以下腳本:

  1. /etc/profile

  2. ~/.profile

如果作為交互Shell啟動,相當於自動執行以下命令:

if [ -n "$ENV" ]; then . "$ENV"; fi

如果作為非交互Shell啟動,則不執行任何啟動腳本。通常我們寫的Shell腳本都以#! /bin/sh開頭,都屬於這種方式。