啟動腳本是bash啟動時自動執行的腳本。用戶可以把一些環境變數的設置和alias、umask設置放在啟動腳本中,這樣每次啟動Shell時這些設置都自動生效。思考一下,bash在執行啟動腳本時是以fork子Shell方式執行的還是以source方式執行的?
啟動bash的方法不同,執行啟動腳本的步驟也不相同,具體可分為以下幾種情況。
交互Shell是指用戶在提示符下輸命令的Shell而非執行腳本的Shell,登錄Shell就是在輸入用戶名和密碼登錄後得到的Shell,比如從字元終端登錄或者用telnet/ssh從遠程登錄,但是從圖形界面的窗口管理器登錄之後會顯示桌面而不會產生登錄Shell(也不會執行啟動腳本),在圖形界面下打開終端窗口得到的Shell也不是登錄Shell。
這樣啟動bash會自動執行以下腳本:
首先執行/etc/profile,系統中每個用戶登錄時都要執行這個腳本,如果係統管理員希望某個設置對所有用戶都生效,可以寫在這個腳本裡
然後依次查找當前用戶主目錄的~/.bash_profile、~/.bash_login和~/.profile三個檔案,找到第一個存在並且可讀的檔案來執行,如果希望某個設置只對當前用戶生效,可以寫在這個腳本裡,由於這個腳本在/etc/profile之後執行,/etc/profile設置的一些環境變數的值在這個腳本中可以修改,也就是說,當前用戶的設置可以覆蓋(Override)系統中全局的設置。~/.profile這個啟動腳本是sh規定的,bash規定首先查找以~/.bash_開頭的啟動腳本,如果沒有則執行~/.profile,是為了和sh保持一致。
順便一提,在退出登錄時會執行~/.bash_logout腳本(如果它存在的話)。
比如在圖形界面下開一個終端窗口,或者在登錄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裡設置。
為執行腳本而fork出來的子Shell是非交互Shell,啟動時執行的腳本檔案由環境變數BASH_ENV定義,相當於自動執行以下命令:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
如果環境變數BASH_ENV的值不是空字元串,則把它的值當作啟動腳本的檔案名,source這個腳本。