讓 MSYS2 Bash 像 Git Bash 一樣顯示 Git 分支名稱

2023-02-05 15:00:29

Git for Windows 的 Bash 有一個很實用的功能,如果當前目錄處於 Git 倉庫中,那麼命令列中會顯示當前 Git 分支的名稱(見下圖)。

然而原版的 MSYS2 Bash 沒有這個功能(見下圖),不過我們可以自己動手設定出相同的效果。

設定方法

開啟 MSYS2 的家目錄,找到 .bashrc 檔案,在其中插入以下程式碼:

function parse-git-branch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}

function prompt-sign() {
    net session > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo "#"  # administrator
    else
        echo "$"  # common user
    fi
}

export PS1="\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[35m\]$MSYSTEM \[\e[33m\]\w\[\e[36m\]\$(parse-git-branch)\012\[\e[0m\]$(prompt-sign) "

接著,關閉並重新開啟命令列視窗,或者在命令列中執行 source ~/.bashrc 命令,即可看到更改後的效果(見下圖)。

原理

上述程式碼通過修改 PS1 這個 shell 環境變數來設定命令提示字元,PS 是 prompt string 的縮寫。MSYS2 的 PS1 的預設值是 \[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[35m\]$MSYSTEM\[\e[0m\] \[\e[33m\]\w\[\e[0m\]\n\$ 。在 PS1 中,以反斜槓開頭的字元有特殊含義,例如:

  • \u:使用者名稱
  • \h:主機名
  • \w:當前目錄
  • \n:換行
  • \$:對 root 使用者顯示 #,對普通使用者顯示 $

更多相關內容參見 Controlling the Prompt (Bash Reference Manual)

PS1 支援用 ANSI 跳脫序列(ANSI escape code)設定文字的顏色,詳見 ANSI escape code - Wikipedia

瞭解了 PS1 的相關規則後,自定義命令提示字元就不是一件難事了。寫一個獲取 Git 分支名稱的函數 parse-git-branch,然後用 \$(parse-git-branch) 將分支名稱插入 PS1 中即可。請注意雙引號字串中 $(expr)\$(expr) 的區別,如果用的是 $(expr) 語法,則只會在第一次讀取變數時對 expr 求值一次,如果用的是 \$(expr) 語法,則每次讀取變數時都會對 expr 求值。由於我們希望每次更改目錄後都重新讀取 Git 分支的名稱,因此應使用 \$(expr) 語法。

到了這裡你會碰到 MSYS2 的一個 BUG,在 \$(parse-git-branch) 之後使用 \n 換行會報如下的錯誤:

bash: unexpected EOF while looking for matching `)'

這個 Stack Overflow 回答給了一種變通的辦法,使用 \012(換行符的 ASCII 碼)來代替 \n 即可。

接下來你會碰到 MSYS2 的另一個 BUG。正常來說,當以管理員身份執行 MSYS2 Bash 時會顯示一個 #(見下圖),當以普通使用者身份執行時則顯示一個 $,這與 Linux Bash 面對 root 使用者/普通使用者的行為十分相似。

前文提到,可以在 PS1 中使用 \$ 為 root 使用者和普通使用者顯示不同的提示符號。但是不知何故 MSYS2 不支援這個功能,始終顯示的是 $,所以需要額外寫一個函數判斷當前是否為管理員身份(即上面的 prompt-sign 函數),我用的是這個回答中的方法。