iT邦幫忙

2

老師!我想知道!如果只使用原生的終端機要怎麼客製化 Prompt 呢!

這篇文章是來自同事的許願,到底能不能不要安裝那些 iTerm2、zsh、oh-my-zsh、字型等等,只用原本的終端機還有 bash 就做到差不多的效果呢?

答案是可以的喔!透過寫一些簡單的設定到 .bashrc 檔案,就可以讓我們的 prompt 顯示你想顯示的內容:例如你想顯示自己的名字、想要顯示時間、上一個指令的狀態等等,也可以幫他們上色,如果想要有很多可愛的小圖示或是想要右邊也顯示一些資訊,只依靠 bash、並且不另外安裝字型的話,可能就比較困難、麻煩一點,所以這裡我們就只介紹怎麼簡單透過設定 .bashrc 來製作自己喜歡的 prompt 吧!


我們先來理解一下 .bashrc 還有 .bash_profile 有什麼分別,當我們打開終端機、或是像是利用 ssh 指令從遠端連線進來到系統、或是執行一份 shell script 時,都會去觸發 bash 的啟動,當啟動時,他會在系統下去尋找一些特定的檔案,並執行檔案的內容,類似為啟動後做一些初始化的設定,而 bash 會去尋找哪些檔案,則是根據啟動了什麼類型的 shell 而定。

Shell 可以是 interactive 或是 non-interactive,按照字面上的解釋就是互動式還有非互動式。互動式的 shell 就像一般我們使用終端機那樣,他可以從終端機讀取使用者的輸入、也可以輸出一些訊息到使用者的終端機,非互動式的就是像執行一個 script file 一樣。

一個互動式的 shell 又可以分做 login shell 及 non-login shell,login shell 是使用者不管透過遠端連線或是本地端打開的方式登入終端機都算是,而 non-login shell 是透過 login shell 所觸發,像是我們在終端機裡面下指令 bash 會進入 bash 的環境、或是像我們在終端機打開一個 Gnome tab 一樣。

我們每次打開終端機的時候,是不是最上面都會顯示 Last login ...,我們打開一個終端機,他就是一個 interactive login shell 哦!

若是平常我們直接下指令 bash,會進入 bash,這時你會發現你的 prompt 變成 bash-3.2,你可以直接在這個狀態下執行任何 bash 的指令,如果要離開,則是使用指令 exit 就可以又回到原本的 prompt 了。

當一個 interactive login shell 被打開後, bash 會先在 /etc 路徑底下找尋名為 profile 的檔案,如果檔案存在,則 bash 會去執行檔案內的指令,不管 /etc/profile 存不存在,接著都會繼續以下的步驟, bash 會接著在家目錄底下依照順序尋找 .bash_profile.bash_login 還有 .profile 這三個檔案,但只會執行第一個找到、而且要是可讀的那個檔案裡的指令。

那如果是 interactive non-login shell 的話,那 bash 會去尋找家目錄下有沒有一個可讀的 .bashrc 檔案,若有的話就去執行裡面的指令。

一般來說,我們可以把只需要被執行一次的指令放在 .bash_profile 裡面,像是設定 $PATH 環境變數,那如果是每一次打開一個 shell 的時候都需要被執行的,像是客製化 prompt 、一些設定別名的指令,如: alias,我們就可以寫在 .bashrc 裡面。

另外我們也可以在 .bash_profile 裡面加上以下這段指令,這樣就可以確保每一次登入到終端機時,.bash_profile 還有 .bashrc 都會被執行到了:

 if [ -f ~/.bashrc ]; then
     . ~/.bashrc
 fi

-f 是用來判斷家目錄底下是否有 .bashrc 這個檔案存在,並且要是 regular file。
~/ 是家目錄路徑底下,regular file 則是像一般的文字檔、圖片啊、可執行檔等等,如果對檔案類型有興趣想了解的話,可以另外自己閱讀文章來了解一下哦!

以上參考文章 .bashrc vs .bash_profile

檔案類型的參考:Regular file


寫了那麼多,終於可以開始介紹怎麼客製化你想要的 prompt 了!

主要我們是透過在 .bashrc 檔案裡面修改 PS1 的這個環境變數,來達到修改 Prompt 樣貌的結果,除了 PS1 還有 PS2 PS3 PS4 ,分別可以設定不同的部分,但我們這次只關注 PS1 的設定,對其他三個有興趣的人可以自己另外查查資料喔!

設定 PS1 主要是透過一些原先 bash 就定義好的參數,可以查看 bash 的說明,使用指令 man bash,並看 PROMPTING 的部分。

以下列出一些比較常使用的:

  1. \d 顯示日期,格式: Weekday Month Date
  2. \D{format} 會依照給定的 format 來顯示時間,這裡的格式是要 strftime(3) 可以判讀的格式。 -> 參考: strftime(3) — Linux manual page
  3. \h 顯示簡短 hostname,顯示到第一個 . 之前。
  4. \H 顯示完整 hostname。
  5. \t 顯示時間,以 24 小時制顯示 HH:MM:SS 格式。 (如: 18:46:27 )
  6. \T 顯示時間,以 12 小時制顯示 HH:MM:SS 格式。 (如: 06:46:27 )
  7. \@ 顯示時間,以 12 小時制,並以 AM / PM 方式顯示 HH:MM 格式。 (如: 06:46 下午)
  8. \A 顯示時間,以 24 小時制顯示 HH:MM 格式。 (如: 18:46 )
  9. \u 顯示目前使用者的名字。
  10. \w 顯示目前完整的工作路徑,如果在家目錄下,則以 ~ 顯示。
  11. \W 顯示簡短工作路徑,也就是顯示目前所在的路徑的最後一層,如果在家目錄底下,一樣是以 ~ 顯示。
  12. \e 是 ASCII 跳脫字元,後面我們要來設定顏色時會用到。

接下來就是按照自己喜好來做排列組合囉,例如我想要顯示的樣子是:目前時間 Chrissspy 目前所在路徑 $ ,那我們就可以把 PS1 設定成:\A Chrissspy \W $,顯示出來的效果是:

因此你就可以善用各種文字、符號、以及上面的參數,來表達出你喜歡的 Prompt!


但即使我們能夠客製出我們想要的樣子了,還是缺少一點色彩讓我們更容易辨識,不曉得大家有沒有在類似 ptt 的地方發過文呢?以前我也曾在其他 BBS 論壇發過文,如果你要加上顏色的話,就要用 ANSI escape code 來設定,如果要設定 PS1 文字的顏色也是一樣的方式喔!

先來看這張 256 個顏色的數字對照表,取自維基百科 Colors 8-bits 的部分

接下來就來介紹要怎麼透過 ANSI escape code 來設定自己想要的顏色吧!

  1. 設定前景,也就是文字的顏色:\e[38;5;nm 其中 n 要換成自己想要的顏色的數字。
  2. 設定背景,也就是文字會有個底色: \e[48;5;km 其中 k 也是替換成自己想要的顏色的數字。
  3. 如果同時要設定前景和背景,可以寫: \e[38;5;nm;48;5;km
  4. 結束目前設定: \e[m

這個結束設定的 \e[m,舉個例子來說明他的用處:比如說我們今天有一串文字 Apple,A 想用紅色,第二個 p 用黃色,其餘字母都不設定顏色,那麼你可能會想寫這樣:

但出來的結果卻是:

怎麼會是前兩個字都是紅色,後面三個字都是黃色呢?那就是因為我們沒有加上結束目前設定的 \e[m,所以顏色就會套用文字直到下一個顏色設定出現,所以要達成我們剛剛的需求,正確寫法應該是:

出來的結果就會如同我們預期的囉:


講到這裡,來給大家一個範例吧!如果我想要我的 prompt 設定成 目前時間 Chrissspy 目前所在路徑 $,並且時間的部分要白色底、黑色文字,而我的名字 Chrissspy 要天空藍色為底、文字為深藍色,目前所在路徑是粉紅色底、白色文字,那該怎麼寫呢?

先寫出文字的部分: \A Chrissspy \W $,接著我們一個部分一個部份加上想要的顏色:

  1. 時間的部分:白色底黑色文字 -> \e[38;5;0;48;5;15m\A\e[m
  2. Chrissspy 部分:天空藍為底色、文字為深藍色 -> \e[38;5;18;48;5;159mChrissspy\e[m
  3. 目前所在路徑: 粉紅色底、白色文字 -> \e[38;5;15;48;5;211m\W\e[m
  4. 整個組合起來就是 \e[38;5;0;48;5;15m\A\e[m\e[38;5;18;48;5;159m Chrissspy\e[m\e[38;5;15;48;5;211m \W\e[m $

出來的結果是:

看起來怪怪的吧,那是因為空格也會被設定上背景顏色喔,那麼來調整一下,把空格都移到顏色設定外面,變成: \e[38;5;0;48;5;15m\A\e[m \e[38;5;18;48;5;159mChrissspy\e[m \e[38;5;15;48;5;211m\W\e[m $

結果會變成:

可以善用一些特殊符號,例如三角形,還有空格的組合,可以配合出漂亮又易懂的 prompt,像是:
\e[38;5;0;48;5;15m\A ▶ \e[m\e[38;5;18;48;5;159m Chrissspy ▶ \e[m\e[38;5;15;48;5;211m \W \e[m $

出來的結果是:

-> 特殊符號參考


以上的設定,我們只要在家目錄底下打開一個檔名為 .bashrc 的檔案,並在裡面放入這一行指令 export PS1="xxxxx",其中 xxx 要替換成你自己設計好的 prompt,以我們剛剛設計好的為例:
就是在檔案裡加上:

 export PS1="\e[38;5;0;48;5;15m\A ▶ \e[m\e[38;5;18;48;5;159m Chrissspy ▶ \e[m\e[38;5;15;48;5;211m \W \e[m $ "

儲存檔案後,在終端機家目錄下指令 . ~/.bashrc,你就可以看到你的 prompt 變成你想要的樣子囉!

如果你發現你每次打開終端機,你的 .bashrc 好像都沒有被載入,那你可以先看看你的家目錄底下有沒有 .bash_profile 這個檔案,如果有就編輯他,沒有的話就開一個檔案,名字是 .bash_profile,並在裡面加上指令如下:

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

指令加上去之後儲存檔案,並結束終端機,之後每次打開終端機,你應該都可以看見你的 .bashrc 內的設定都有被執行到囉!


最後,如果你想要加上一些跟 git 相關的資訊到 prompt 裡面,像是分支資訊、還有 git 目前狀態的話,網路上有許多人寫好了 function 可以去得到 git 的相關資訊,你只要在 .bashrc 檔案的開頭加上以下的程式碼(放在本篇教學的最後),最後在 PS1 裡設定時,寫法是

\`parse_git_branch\`

如果我把這一段加到我剛剛設定好的 prompt 裡面,我的 PS1 會變成:

export PS1="\e[38;5;0;48;5;15m\A ▶ \e[m\e[38;5;18;48;5;159m Chrissspy ▶ \e[m\e[38;5;15;48;5;211m \W \e[m (\`parse_git_branch\`) $ "

出來的效果是:

一樣可以加上顏色效果,就放在

\`parse_git_branch\`

的前後。

以下為要加在 .bashrc 的程式碼中的一部分,如果你想要修改不同狀態顯示的符號的話,像是你想修改目前目錄如果有 untracked file 時,原本用以下的程式碼會顯示 ?,如果你想改成顯示 untracked?,就修改底下的這個部分裡的 ?

if [ "${untracked}" == "0" ]; then
	bits="?${bits}"
fi

改成

if [ "${untracked}" == "0" ]; then
	bits="untracked?${bits}"
fi

出來的效果會如下圖,又有驚嘆號又顯示 untracked? 是表示目前工作路徑底下的 git 的檔案狀態是同時有 dirty 和 untracked 的檔案:


以下是如果你想顯示 git 資訊可以使用的程式碼:

# get current branch in git repo
function parse_git_branch() {
	BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
	if [ ! "${BRANCH}" == "" ]
	then
		STAT=`parse_git_dirty`
		echo "[${BRANCH}${STAT}]"
	else
		echo ""
	fi
}

# get current status of git repo
function parse_git_dirty {
	status=`git status 2>&1 | tee`
	dirty=`echo -n "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
	untracked=`echo -n "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
	ahead=`echo -n "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
	newfile=`echo -n "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
	renamed=`echo -n "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
	deleted=`echo -n "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
	bits=''
	if [ "${renamed}" == "0" ]; then
		bits=">${bits}"
	fi
	if [ "${ahead}" == "0" ]; then
		bits="*${bits}"
	fi
	if [ "${newfile}" == "0" ]; then
		bits="+${bits}"
	fi
	if [ "${untracked}" == "0" ]; then
		bits="?${bits}"
	fi
	if [ "${deleted}" == "0" ]; then
		bits="x${bits}"
	fi
	if [ "${dirty}" == "0" ]; then
		bits="!${bits}"
	fi
	if [ ! "${bits}" == "" ]; then
		echo " ${bits}"
	else
		echo ""
	fi
}

如果你實在懶得研究要怎麼設定自己想要的樣子要怎麼轉換成文字設定到 PS1 ,那你可以參考這個網站:
Easy Bash Prompt Generator

可以讓你透過簡單拖拉、設定顏色,幫你產出一串 PS1 的設定喔!

以上是今天的分享,如果有錯誤再麻煩大家留言告訴我哦,感謝大家!希望大家喜歡今天的分享!


1 則留言

0
flipsyde
iT邦新手 5 級 ‧ 2020-10-30 17:29:20

太感動拉~~~~~~

我要留言

立即登入留言