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