以前很喜歡折騰命令提示符,bash/zsh 下面很多提示符都試過,一開始挺有意思,但用久了都有些彆扭,他們不當影響觀感,而且奇慢無比,比如下面這款集成 git branch 信息的:

典型的性能殺手,低配電腦/cygwin/msys 環境提示符計算分支很慢,按一次回車卡一秒。就連好點的機器也能感到明顯的延遲,我想問一下:這個代價值得麼?branch 真的是那麼重要的信息需要每行命令都看?即便不在 git 倉庫中,每按一下回車也要空跑一下計算程序?還用了奇怪的非標準字元,導致換個終端,你可能顯示不正常。

很多 zsh 的新用戶常常抱怨「zsh 太慢了」,我跟他們說,為啥我的 zsh 很流暢呢?把你的 prompt 給禁止掉,看看還慢不慢?順便把 oh-my-zsh 刪了,最慢的就是這個。這還算好的提示符了,還有更花哨的:

連時間,分支,還有 ruby 的 virtualenv(好像)全都給我塞進去了,我想問一下,既然 ruby 的 virtualenv 都顯示了,為何不給我顯示 python 的 virtualenv 呢?我還想看 CPU 佔用呢,要不要每行給我輸出一個?你們知道這玩意兒在我的 nas 下面多慢?按一次回車卡 1.2秒。

我發現 oh-my-zsh 的用戶特別喜歡折騰 PROMPT,花哨程度只有更高,沒有最高:

我已經開啟了命令行語法高亮,PROMPT 還在那裡花裏胡哨的話,容易讓我眼花繚亂,人家服裝設計一般都要控制衣服顏色不超過3種,命令提示符花裏胡哨的幹嘛呢?

試過了很多命令提示符以後,我開始思考,怎樣纔是我想要的 PROMPT?

  1. 他真的需要集成那麼多信息麼?重要的信息太多,一直往 prompt 裏塞是個辦法麼?
  2. 色彩高亮到底是為了讓人醒目?還是讓人覺得混亂?
  3. 我到底是要好看還是要效率?

想完這三個問題以後,我把這些亂七八糟的 prompt themes 全部刪除了, 然後開始思考如果我自己設計命令提示符,我需要設計成什麼樣呢?於是整理了一個清單:

  1. 速度:絕對不能卡,哪怕在我的路由器上,按回車一定要流暢,否則再怎麼強大都免談,所以必須盡量避免每次顯示 PROMPT 的時候都要啟動新進程。
  2. 精簡:不能佔用太多空間,同屏/同行看到的信息越多我工作效率越高,prompt 就不該佔用太多,應該盡量把空間留出來,git branch 這些完全可以 alias 一下,要看時 gb 就夠了。
  3. 素雅:避免讓我眼花繚亂。
  4. 兼容:我是混用 bash/zsh 的,一個提示符不能讓我 zsh 可以用,換臺機器就沒法用了。

總之就是一句話:prompt 應該是為我提高效率的,不是來拖我後腿的東西。

本著上面四條原則,我重新設計的兩個主題,然後感覺整個世界都清凈了,再也沒有折騰過 prompt 了,一直安心的用了到現在,這裡推薦給大家:

默認主題

其實就是 debian/ubuntu 的默認主題,顯示:用戶名,主機名和路徑,有時候真的就夠了,末尾如果是普通用戶的話,顯示一個「$」,如果是 root 則顯示一個 "#"。

避免去到 centos 或者路由器上不一致,我們新建一個 prompt_default.sh 文件,內容如下:

if [ -n "$BASH_VERSION" ]; then
export PS1=u@h:w$
else
if [ "$UID" -eq 0 ]; then
export PROMPT=%f%n@%m:%~%#
else
export PROMPT=%f%n@%m:%~$
fi
fi

然後,再你的 bash 配置文件裡面 source 一下該文件,當然,你也可以全部塞在你的 bashrc 或者 init.sh 裡面,只是分離文件的話,換主題方便些,改下 source 後面的文件名就行。

Bash 下面如果是 root 會自動將 $ 替換成 #,zsh 需要具體指明一下,效果如下:

什麼?太素了?沒顏色?好吧,我們新建 prompt_color.sh,支持 8 色終端:

if [ -n "$BASH_VERSION" ]; then
export PS1=[e[01;32m]u@h[e[00m]:[e[01;34m]w[e[00m]$
else
if [ "$UID" -eq 0 ]; then
export PROMPT=%F{10}%n@%m%f:%F{12}%~%f%#
else
export PROMPT=%F{10}%n@%m%f:%F{12}%~%f$
fi
fi

現在看起來好了那麼一點:

你也可以在 bash 那段加個判斷,uid 是 0 的話(root),將用戶名顯示未紅色:

export PS1=[e[01;31m]u@h[e[00m]:[e[01;34m]w[e[00m]$

這樣更醒目些,提醒你現在是 root 了,凡事要小心:

搞了半天還和默認不配置效果一樣,這不是浪費時間麼?別急,上面只是些基本配置和配置的組織方式,上面這些太土沒關係,至少支持各種終端,接下來我們弄個稍微洋氣點的,支持 256 色,新建文件 prompt_256.sh:

if [ -n "$BASH_VERSION" ]; then
export PS1=[e[38;5;135m]u[e[0m]@[e[38;5;166m]h[e[0m [e[38;5;118m]w[e[0m] $
else
if [ "$UID" -eq 0 ]; then
export PROMPT="%F{135}%n%f@%F{166}%m%f %F{118}%~%f %# "
else
export PROMPT="%F{135}%n%f@%F{166}%m%f %F{118}%~%f $ "
fi
fi

效果如下:

這個配色參考的 prezto 裏一個比較素雅的主題 skwp,很清爽吧?但是速度比它快多了,以上這些都是類似 debian/ubuntu 的默認提示符,只是配色稍加修改,這是我的第一個主題。

後期處理

設置完 $PS1 或者 $PROMPT 環境變後就結束了麼?我們還需要在他們前面加一些終端控制符,好讓每次切換路徑的時候更改下終端標題,比如:

export PS1="[e]0;u@h: wa]$PS1"

通過終端控制命令,在每次顯示命令提示符,都會把終端軟體的標題設置成包含:用戶,主機名以及當前路徑的一串字元串。這和以前一模一樣,和我一樣嫌棄它太長的話,可以修剪下,只顯示:用戶名+主機名,標題不會隨著當前路徑改變而改變:

export PS1="[e]0;u@ha]$PS1"

這是我最常用的方式,zsh 的 $PROMPT 變數裏不能這麼設置改變標題,解析不了,一般是寫到 precmd_function 這個 hook 裏,各大框架都有做,可以繼續沿用。唯一要做的是設置一下 $RPROMPT:

export RPROMPT="%F{red}%(?..%?)%f"

這樣就能在右邊用紅色顯示上一個程序的返回碼了,如果是0的話(程序正常)就不顯示,非零代表錯誤返回值。

到這裡我們就完成了後期處理,這些代碼可以放到 bashrc/init.sh 裡面,但是要保證是在 source 上面那些主題之後再設置,避免被覆蓋。

Fish 路徑摺疊

除了上面的默認主題外,我常用的還有一款類似 fish shell 的主題:

fish 默認命令提示符最明顯的特點就是「路徑摺疊」,即提示符只保留最後一級目錄的完整名稱,其他父目錄全部摺疊成一個字母的前綴。

有時候路徑太長的話,默認 prompt 基本上要佔到我半個屏幕的寬度,如果採用 tmux 左右分屏,或者 vim/emacs 的一個分屏內嵌終端,那麼我敲命令的空間,還沒有路徑名佔用的那麼多。比如你用 MacOS 到 xcode 文件夾下面找個文件,路徑提示符都快頂到最右邊了。

所以我當年才見到 fish 的這個「路徑摺疊」就十分喜歡,它解決了痛點,保留了必要的信息,又不至於佔用太多空間,我可以再 xcode 裏最深的一個文件夾裏漫遊,也不會擔心 prompt 太過礙眼。

還有一個好處是用色很少,fish/zsh 下面本身就有語法高亮,如果 prompt 這裡再花哨一些的話,真的會亂不清楚兩行命令的交界在哪裡。

所以 fish 確實是一個經過深思熟慮設計出來的 shell,那麼我們把它移植到 bash/zsh 下面,注意 oh-my-zsh 下面有一個 fishy 的主題可以做上面的事情,大家千萬別用,因為裡面是調用 perl 來計算路徑摺疊,每次你按回車都要執行一次 perl 什麼概念?

明明純 shell 就可以了,我們新建 prompt_fish.sh 文件,先定義個函數:

function _fish_collapsed_pwd() {
local pwd="$1"
local home="$HOME"
local size=${#home}
[[ $# == 0 ]] && pwd="$PWD"
[[ -z "$pwd" ]] && return
if [[ "$pwd" == "/" ]]; then
echo "/"
return
elif [[ "$pwd" == "$home" ]]; then
echo "~"
return
fi
[[ "$pwd" == "$home/"* ]] && pwd="~${pwd:$size}"
if [[ -n "$BASH_VERSION" ]]; then
local IFS="/"
local elements=($pwd)
local length=${#elements[@]}
for ((i=0;i<length-1;i++)); do
local elem=${elements[$i]}
if [[ ${#elem} -gt 1 ]]; then
elements[$i]=${elem:0:1}
fi
done
else
local elements=("${(s:/:)pwd}")
local length=${#elements}
for i in {1..$((length-1))}; do
local elem=${elements[$i]}
if [[ ${#elem} > 1 ]]; then
elements[$i]=${elem[1]}
fi
done
fi
local IFS="/"
echo "${elements[*]}"
}

該函數用於計算路徑,並且同時兼容 bash/zsh,速度比調用 perl 快二十多倍,接著設置:

if [ -n "$BASH_VERSION" ]; then
if [ "$UID" -eq 0 ]; then
export PS1=u@h [e[31m]$(_fish_collapsed_pwd)[e[0m]#
else
export PS1=u@h [e[32m]$(_fish_collapsed_pwd)[e[0m]>
fi
else
if [ $UID -eq 0 ]; then
export PROMPT=%f%n@%m %F{1}$(_fish_collapsed_pwd)%f#
else
export PROMPT=%f%n@%m %F{2}$(_fish_collapsed_pwd)%f>
fi
fi

恩,保存文件,source 一下看看效果:

成了!bash 下路徑效果和 fish 一樣了,包括 root 下的狀態:

可以看出,root 下也和 fish 一樣採用了紅色顯示當前路徑,並且把 ">" 換成了 "#" 符號。關鍵是速度非常快,比 oh-my-zsh 下面那個模擬 fish 的 fishy 主題快上幾十倍,和默認主題一樣的速度。主要是執行 shell 內建函數代替了 oh-my-zsh 裡面的調用 perl 來計算路徑摺疊。

這是我常用的第二個主題。

Shell 混用與語法高亮問題

我的兩個主題一般是 bash 用前面那個 256 色的,好看醒目,而 zsh 用這個 fish 路徑摺疊的,以示區別。而且 zsh 和 fish 一樣啟用了命令語法高亮,適合選擇各色少的主題:

這是我 zsh 使用 fish 路徑摺疊主題搭配語法高亮,還有 $RPROMPT 的效果。可以想像,本身高亮就已經夠搶眼了,如果 prompt 還是很花哨的話,容易讓眼睛抓不住重點。

Bash 下面沒有語法高亮,本身就比較素,所以我用 256 那個比較好看又醒目。

所以如果和我一樣混用 bash/zsh 的同學們也可以參考做一些取捨,如果 bash/zsh 都用 fish 主題的話,注意稍微調整一下,以示區別,比如 bash 下面都用 ">" 符號,zsh 下面都用 "#" 符號之類的,以示區別,root 用戶以顏色區分就好。

話題總結

今天先說了各種花哨主題存在的問題,思考了主題存在的價值和意義,最後和大家分享了我正在使用的兩款比較乾淨素雅的主題,並且通過上面的設置過程,讓大家瞭解了 bash/zsh 下面該如何製作自己的主題,如何設置顏色?如何調用函數?怎麼區別 root 和非 root ?及相應的後期處理。

相信大家可以輕鬆的設計出讓自己滿意的 prompt 了。

---

補充1:覺得自己的 oh-my-zsh 主題很快的同學們,可以做個試驗,系統設置裏將鍵盤重複設置成最快,然後到你的 oh-my-zsh 下面按下回車不放,兩秒鐘後放手,看看 PROMPT 生成的相應如何,再對比下什麼都沒有的 bash 有何不同?

補充2:實在覺得 git 信息離不開的人,可把 git status/branch 綁定到 F5 快捷鍵啊,既讓你方便看到 branch,又避免了每按一次回車就啟動一堆新進程的開銷。

推薦閱讀:

相關文章