理解 Linux/Unix 登入指令碼

youou發表於2021-09-09

本文由碼農網 – 韓先生原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

不知道你有沒有遇到過這樣的場景,當你需要設定一個環境變數,或者執行一個程式設定你的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管理選項。

譯文連結:http://www.codeceo.com/article/linux-unix-login-script.html
英文原文:Understanding *NIX Login Scripts
翻譯作者:碼農網 – 韓先生
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章