如何理解Linux/Unix登錄腳本
不知道你有沒有遇到過這樣的場景,當你需要設置一個環境變量,或者運行一個程序設置你的shell或桌面環境,但是不知道在哪里是最方便設置的位置。
有一些常見的情況,例如從Debian的包管理程序到Iaas的管理中,很多任務需要設置環境變量才能正常運行。
有時,程序通常只需要在首次登陸時運行一次,例如xrandr命令。
此外,有的程序偶爾會被注入到shell中,例如rbenv,rvn或 SitePoint’s自己的 envswith 程序。
讓我們來看看在Debian GNU/Linux Jessie安裝中出現的一些常見選項,并嘗試理解這一切。
/etc/profile
默認情況下,Debian提供/etc/profile文件,這個文件用來設置$PATH變量($PATH通常用來聲明命令的搜索路徑),可以立即生效。下面的代碼是/etc/profile的一部分。
if [ "`id -u`" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
fi
export PATH
為了方便,root用戶(ID為0)和其他任何用戶的路徑都不同。這是因為系統二進制目錄(sbin目錄)位置傳統上是作為系統管理程序、或必須以root身份運行的程序存放的保留位置。而games路徑對于root用戶來說是省略的,因為不到非必要的時候,絕不可能使用root用戶來運行游戲程序。
接下來,/etc/profile處理$PS1變量的設置,$PS1變量是用來設置主提示字符串(即用戶登陸時顯示的字符)。除了系統的shell是Bash以外,系統$PS1變量默認設置的是$ (root用戶默認是#)。如果系統的shell使用的是Bash,則/etc/bash.bashrc 文件會替代$PS變量來處理主提示字符串(特殊情況除外)。后面我們會簡短地說一下/etc/bash.bashrc。
所以從這一點上,我們可以推斷/etc/profile在登陸期間(例如使用login命令)會被所有的shell讀取。/etc/profile調用id命令來讀取用戶ID,而不是使用更高效的Bash內置變量${UID}。Bash使用特定來源的配置,而不是定義一個花哨的shell提示符,因為Bash支持反斜杠轉義的特殊字符,例如\u(用戶名) 和 \h (主機名),許多其他的shell都不支持這樣定義。/etc/profile應該嘗試和POSIX兼容,以便與用戶可能自己安裝的任何shell兼容。
Debian GNU/linux通常預裝Dash,Dash是一個僅僅旨在實現POSIX(和一些伯克利)擴展的基本shell。如果我們修改/etc/profile(修改之前先備份)讓PS1=’$ ‘這一行設置不同的值,然后模擬一個Dash登錄(通過dash -l命令),我們可以看到Dash會使用我們自定義的提示。但是,如果我們調用不帶-l參數的dash命令,dash將不會讀取/etc/profile。此時Dash會使用默認值(這意味著此時PS1的值是我們修改之前的值)。
最后一點和/etc/profile相關的趣事是下面的代碼片段:
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
換句話說,任何匹配/etc/profile.d/*.sh的可讀內容都會被當作變量來源。這個非常重要,因為它表明直接編輯/etc/profile從來都不是實際需要的(所以恢復你之前的備份)。上面定義的任何變量都可以通過在一個單獨的文件中配置,然后覆蓋/etc/profile中的設置。這樣做的好處是:它允許系統升級時自動添加相應的變更到/etc/profile文件中。因為Debian的Apt包管理系統通常不會修改默認的配置文件。
~/.bash_profile, ~/.bash_login, and ~/.profile
/etc/profile存在的一個潛在問題是,它位于系統范圍的路徑中。這意味著修改它會影響這個系統上的所有用戶。在個人計算機上,這可能不是太大的問題,但是修改它同時還需要root權限。由于這些原因,每個單獨的Bash用戶賬戶可以創建~/.bash_profile, ~/.bash_login 和 ~/.profil這幾個文件中的任意一個作為Bash的配置文件來源。在列出的順序中第一個被找到的文件會被作為配置文件,其余的都會被忽略。
其他的shell,例如Dash,支持相似的東西,但是只會查找~/.profile文件。這允許用戶為Bash特定的應用場景配置單獨的.bash_profile文件,如果在某些時候需要切換到Dash或其他shell作為登錄shell(例如通過chsh -s dash命令)。可以保留~/.profile作為這些shell的配置文件。
需要牢記的一點是,默認的Debian框架目錄(/etc/skel,用于存放要復制到新用戶賬戶主目錄的文件和目錄)包含.profile文件,但不包含.bash_profile和.bash_login文件。此外Debian使用Bash作為默認的shell,因此,許多Debian用戶習慣于將他們的Bash 登錄shell設置放在.profile文件中。
我曾經看到過一些項目的安裝說明,例如RVN,這個項目建議用戶創建一個.bash_profile文件,但是這樣做是非常危險的,根據上面提到的知識我們知道,這個會改變用戶的shell環境。即使用戶沒有修改.profile文件,它也可能利用默認~/.profile功能,將~/bin添加到$PATH環境變量。一個可能提高安全性的選項是,在創建用戶的賬戶之前,將.bash_profile作為.bash_rc的符號鏈接文件,放到/etc/skel目錄中。
如果我們查看Debian Jessie的默認.profile腳本,我們可以看到下面的代碼片段:
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
這和我們在/etc/profile里面看到的相似,如果shell是Bash,且發現了/etc/bash.bashrc文件,/etc/bash.bashrc文件就被當作Bash的配置文件。這一點的意義將在下一節討論。
/etc/bash.bashrc 和 ~/.bashrc
啟動的時候,Bash會同時讀取/etc/bash.bashrc和~/.bashrc,但是只有在Bash Shell作為交互式Shell而不是登錄Shell啟動時(意味著通過xtem啟動),會依照這種順序,這是Bash Shell的標準行為。然而,Debian分別從 /etc/profile和~/.profile登錄腳本中獲取配置文件。這會顯著地改變行為,使得/etc/bash.bashrc和.bashrc(如果它們存在)總是在Bash啟動時調用,而不管是不是登錄Shell。不要期待這種情況在不同地發行版中是一樣的。
.bashrc是一個添加命令別名的好地方,實際上,一些用戶擁有太多的別名,以至于他們寧愿將別名都放在一個單獨的文件中去。Debian的默認.bashrc會查找.bash_alias,如果這個文件存在的話,會將它作為別名配置來源。所以你可以在這個文件中隨意保存所有的Bash別名。如果用戶愿意的話,.bashrc文件也是用戶重寫shell變量,例如$PS1或者$HISTSIZE的絕佳位置。Debian的默認.bashrc有超過100行,但是仍然可以非常清晰地閱讀,且有良好地注釋。見名知意,.bashrc不是其他非Bash shell的配置文件來源。
~/.xsession 和 ~/.xsessionrc
如果你是一個GNU/Linux桌面用戶,通過顯示管理器本地登錄(而不是通過getty登錄程序),則/etc/profile和~/.profile不會像預期的那樣工作。一些顯示管理器會直接將這些文件視為錯誤地配置文件,例如Gnome顯示管理器。但一些其他的顯示管理器,例如LightDm不會這樣。幸運的是,你還有一些其他的選項。
當啟動X Window系統會話時(不管是用顯示管理或從虛擬終端啟動startx),將會執行/etc/X11/Xsessionshell腳本。這基本上相當于登錄shell調用/etc/profile。這個只對X Window生效,并且不是將其作為源配置文件,而是直接執行。但是它也相當復雜,類似于/etc/profile怎么從/etc/profile.d目錄中的腳本讀取配置,怎么從/etc/X11/Xsession.d/目錄下的/etc/X11/Xsessions腳本中讀取配置。在/etc/X11/Xsession.d目錄下的所有腳本名稱都以數字開頭,因此所有的腳本都會按照數字順序來讀取。
Debian Jessie包含一個名叫40×11-common_xsessionrc的文件,這個文件做的工作就是檢查~/.xsessionrc是不是可讀的,如果是就用它作為配置文件的來源。這就使得~/.xsessions是一個加載環境變量或者運行一個一次性使用程序(例如xrandr或xmodmap)的完美位置(僅適用于X會話)。如果你希望的話,你同樣可以將/etc/profile或~/.profile作為來源。那么任何指定的環境變量也都會被你的會話管理器繼承(如果還沒有繼承的話)。請注意,默認情況下.xsessionrc是不存在的,需要你自己創建這個文件。
如果我們繼續瀏覽/etc/X11/Xsession中的文件, 我們會發現50×11-common_determine-startup會決定加載哪個會話管理器。如果~/.xsessions文件存在而且是可執行的,它會被保存并且隨后作為99×11-common_start的一部分執行,當~/.xsession用于運行會話管理器,X會話將會被注銷。并且當這個腳本終止時,你會返回到顯示管理器登錄界面。
和~/.xsessionrc相似,~/.xsession默認也是不存在的,在你需要的時候你可以創建一個。你可能會創建一個類似下面給的簡單的.xsession腳本
# Start our session manager of choice.
#
exec x-session-manager
其中x-session-manager默認設置為通過update-alternatives命令配置的任何內容,這樣,你可以輕松地更改系統范圍默認地會話管理器,只需要將x-session-manager替換為/usr/bin/startfce4(切換到XFCE),其他的用戶賬戶將完全不受影響。
當然,許多顯示管理器提供從登錄界面直接選擇公共會話管理器的能力,所以這個文件通常是不必要的。然而.xsession提供了更多地靈活性,你可以用任何程序調用這個文件,而不僅僅是會話管理器。例如,在這里你可以在while循環中調用chromium或者iceweasel,而不是執行基本的kiosk模式設置。
~/.bash_logout
我們前面介紹了當用戶運行交互式Bash登錄shell時讀取的文件,但是如果你想在注銷以后仍然運行程序該怎么辦?對于這個用例,~/.bash_logout文件就非常方便了。在Debian中默認的配置僅用于清除屏幕(我認為從安全角度來說很重要),但是可以輕微地想象以下就知道能用于其他目的,例如,在你離開你的機器之前顯示一個幾秒鐘的提醒。
主要的限制因素在于.bash_logout僅在注銷交互式shell時讀取,并且并不能假定它在注銷X會話時會被加載。
其他選項
上面那些已經為你介紹了大部分的通用選項。其他的選項可能會存在,取決于你的安裝環境(例如/etc/environment),但是我不認為他們可能在其他的平臺上存在,并且極少有需要去接觸它們。
示例
那么你應該在哪放置你的系統范圍環境變量?如果你希望一個環境變量可以影響所有用戶,/etc/profiled./someifle.sh會是一個好的選擇。但是,這假設你是使用一個登錄管理器以/etc/profile作為配置來源。如果不是這樣,你可以(作為一個管理員)添加一個腳本到/etc/X11/Xsession.d/來替代/etc/profile作為配置來源。
如果你希望一個腳本可以找到一個私人目錄路徑,并且添加它到你的PATH中,你需要考慮這個目錄是不是會移動很多東西,如果你向.profile添加代碼來實現,用戶需要注銷然后再登錄來更改用戶會話期間的PATH。如果你將代碼添加到.bashrc中,這意味著代碼將在用戶每次打開xterm時執行,如果執行大約半秒以上可能就不太理想。所以這是一個權衡取舍的問題。
如果你僅僅是為了你個人登錄會話時的一個環境變量,且它只關心X會話,你可以將它添加到~/.xsessionrc中。這樣做的優點是,它通常將可用于通過X會話管理器啟動的所有程序,因為它在啟動X會話管理器之前被設置,并且被繼承。例如,某些圖形驅動程序可以通過運行
export vblank_mode=0
來禁用vsync。 所以位于.xsessionrc中的變量會影響到所有的程序。
然而如果這一行被添加到.bashrc中,則只有通過xterm登錄的程序會被影響。通過一個窗口管理器啟動的程序照常運行。你可以把它添加到.profile,并且從.xessionrc作為.profile的來源。但是之后,當你的X服務沒有在運行的時候,你就不需要導出環境變量。
希望你現在可以更好地了解了登錄和注銷腳本在Debian GNU/Linux系統上的工作原理。如果你已經為這些登錄和注銷腳本創建、或者遇到任何特別有趣或有創新的用途,請在評論中告訴我們你是如何做到的。
在接下來的系列中,我們將討論dotfile管理選項。