【Linux】載入順序

image-1677741051902.png

https://blog.miniasp.com/post/2021/07/26/Bash-and-Zsh-Initialization-Files

可以完整釐清 Bash 所有啟動檔的載入條件與順序,心裡的感覺格外踏實,非常棒! 👍

完整的 Zsh 啟動檔載入順序

由於許多 macOS 用戶都採用 Zsh 為主,這邊我也特別研究了一下 Zsh 的啟動檔載入順序,基本上 Zsh 會依據以下順序載入:

  1. ~/.zshenv

    任何啟動情境下,都會載入這個檔案,請將各種環境變數請全部設定在這裡。

  2. /etc/zsh/zprofile 與 ~/.zprofile

    如果執行在 Login shell 才會依序執行 /etc/zsh/zprofile 與 ~/.zprofile 檔案。

  3. /etc/zsh/zshrc 與 ~/.zshrc

    如果執行在 Interactive 互動模式下,才會依序執行 /etc/zsh/zshrc 與 ~/.zshrc 檔案。

  4. /etc/zsh/zlogin 與 ~/.zlogin

    如果執行在 Login shell 下,最後才會依序執行 /etc/zsh/zlogin 與 ~/.zlogin 檔案。

  5. ~/.zlogout 與 /etc/zsh/zlogout

    當你用 exit 或 logout 命令登出時,會自動依序執行 ~/.zlogout 與 /etc/zsh/zlogout 檔案。

我只能說,Zsh 的啟動順序實在比 Bash 好理解太多了,也沒什麼地雷! 👍

總結幾種常見情境

  1. 直接呼叫某個 Shell Script

    #!/bin/bash
    [ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"
    shopt -q login_shell && echo "Login shell" || echo "Not login shell"
    
    • Interactive? 👉 No
    • Login shell? 👉 No
    • --noprofile? 👉 No

    執行時完全不會載入任何新的啟動檔設定!

  2. 使用 SSH 登入遠端主機

    ssh user@host
    

    依序載入 /etc/profile --> /etc/bash.bashrc --> ~/.profile [ -> ~/.bashrc ]

    • Interactive? 👉 Yes
    • Login shell? 👉 Yes
    • --noprofile? 👉 No

    注意: Bash 不會主動載入 ~/.bashrc 執行,而是我們通常在 ~/.profile 會載入 ~/.bashrc 執行。

  3. 使用 SSH 登入遠端主機後,執行 bash 命令

    ssh user@host
    

    剛登入,依序載入 /etc/profile --> /etc/bash.bashrc --> ~/.profile [ -> ~/.bashrc ]

    • Interactive? 👉 Yes
    • Login shell? 👉 Yes
    • --noprofile? 👉 No

    然後我們在遠端主機執行 bash 命令,進入下一層 Shell 環境:

    bash
    

    登入後執行 bash 預設是互動模式,會依序載入 /etc/bash.bashrc --> ~/.bashrc

    • Interactive? 👉 Yes
    • Login shell? 👉 No
    • --rcfile <file> ? 👉 No
    • --norc ? 👉 No
  4. 使用 SSH 登入遠端主機後,執行 bash -c '<command>' 命令

    ssh user@host
    

    剛登入,依序載入 /etc/profile --> /etc/bash.bashrc --> ~/.profile [ -> ~/.bashrc ]

    • Interactive? 👉 Yes
    • Login shell? 👉 Yes
    • --noprofile? 👉 No
    bash -c '[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'
    

    登入後執行 bash -c 命令屬於非互動模式,而且沒有 $BASH_ENV 的情況下,不會載入任何啟動檔!

    • Interactive? 👉 No
    • Login shell? 👉 No
    • $BASH_ENV 👉 No
  5. 使用 SSH 執行遠端命令

    ssh user@host '[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'
    ssh user@host 'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'
    

    透過 SSH 執行遠端命令,預設將會跑在 Non-Interactive 模式下,但還是會依序載入 /etc/bash.bashrc --> ~/.bashrc

    • Interactive? 👉 No
    • Login shell? 👉 No

    如果你想在執行遠端命令時載入額外的啟動檔,也可以這樣寫:

    ssh user@host "source /etc/profile; source ~/.profile; /your/script.sh"
    
  6. 使用 SSH 執行遠端命令,透過 bash 呼叫另一個命令

    ssh user@host -t 'bash -c '"'"'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'"'"''
    ssh user@host -t 'bash -c '"'"'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'"'"''
    

    要在單引號(')的字串中間加入一個單引號,必須輸入五個字元 '"'"' 才能代表一個單引號!(噁心的語法)

    在 ssh 執行時加入 -t 參數,可以在遠端執行時取得一個 pseudo-terminal allocation (pts/0)!

    透過 SSH 執行遠端命令,預設將會跑在 Non-Interactive 模式下,但還是會依序載入 /etc/bash.bashrc --> ~/.bashrc

    • Interactive? 👉 No
    • Login shell? 👉 No

    第二層 bash 應該是跑在 Non-Interactive 模式,但是卻依序載入 /etc/bash.bashrc --> ~/.bashrc 檔案,這部分我還沒辦法理解為什麼會這樣,感覺透過 SSH 執行遠端程式,預設就會載入這兩個檔案!

    • Interactive? 👉 No
    • Login shell? 👉 No

    底下這段是讓命令跑在 Interactive 模式,但載入啟動檔的順序竟然跟 Non-Interactive 竟然一樣!

    ssh user@host -t 'bash -i -c '"'"'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'"'"''
    ssh user@host -t 'bash -i -c '"'"'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'"'"''
    

    簡單來說,使用 SSH 直接遠端命令時,使用 -i (互動模式) 或不使用 -i 對載入啟動檔沒有什麼兩樣,不過都會載入兩次。不過還是有一個地方不同,如果你的啟動檔有設定 alias 的話,只有使用 -i 互動模式才能使用。

    例如以下這段命令,就會得到 bash: ll: command not found 的錯誤:

    ssh user@host -t 'll'
    
    ssh user@host -t bash -c 'll'
    

    如果改用 -i 來執行,就可以正常執行 ll 這個 alias 命令:

    ssh user@host -t bash -i -c 'll'
    
  7. 使用 SSH 執行遠端命令,並以 Login shell 啟動 Bash

    ssh user@host bash -l -c 'set'
    

    第一層 bash 依序載入 /etc/bash.bashrc --> ~/.bashrc

    第二層 bash 依序載入 /etc/profile --> ~/.profile [ -> ~/.bashrc ]


修訂版本 #3
由 treeman 建立於 2 T 2023 15:10:48
由 treeman 更新於 10 Q@ 2024 15:23:01