人生不能黑白,終端機當然也是
不知道大家有沒有用過 exa 這個 CLI 工具,他基本上是現代版的 ls(我都直接 alias ls='exa -h'
)。除了是用 Rust 寫的所以效能很好之外,他還會幫結果上色,讓眼睛看起來更舒服,同事不經意看到你的螢幕時也會覺得你很厲害(X

不過今天的重點不是 exa,而是為什麼 exa 的輸出可以這麼五彩繽紛甚至還能畫線?這就要從 跳脫序列(Escape Sequence) 開始說起了
ANSI Escape Sequence
第一次聽到跳脫序列(Escape Sequence)可能會不知道那是啥,但我們其實很常用到它哦~
像大家都知道在寫程式的時候 \n
代表的是換行、而 \t
是 Tab。這類型 \
開頭的字元就被稱做跳脫字元。\n
並不是字面上的 \
跟 n
兩個字元,而是有特別的換行功能,所以如果在終端機中下指令 echo -e "Hello\nWorld"
會看到 Hello
跟 World
是各自一行

但除了 \n
跟 \t
之外其實還有各種奇怪的跳脫字元,而且也不一定都短短的,像顏色也是用跳脫字元來實現的。舉個例子,像 \033[34m
這一串可以把之後的字變成藍色,可以想像成把手上的筆換成藍色的,而 \033[0m
則是把顏色重置。
所以 \033[34mHello\033[0mWorld
這一大串就是先拿藍色的筆寫 Hello
,換回原本的筆後再寫 World
,因此只有前面的 Hello 變成藍色,很神奇吧!

CSI Sequence
像 \033[34m
跟 \033[0m
這種格式被稱作 CSI 序列,他們的共通點是都是以 \033[
開頭、以 m
結尾,中間再填上 SGR 參數,用來設定你想把輸出變成什麼顏色。
那每個數字到底代表什麼顏色呢?我們一起來看看維基上的顏色表,可以看到 34 對應到的是 前景色代碼的藍色,因此 \033[ 34 m
就是把字體顏色設為藍色。

那如果你想要的是 黃底藍字 呢?那就用分號 ;
把黃色背景(43)跟藍字(34)串起來作為 SGR 參數就可以了。實際用 \033[34;43mHelloWorld
馬上就可以看到黃底藍字的 Hello World 哦~

除了顏色之外,SGR 參數還可以用來調整很多東西哦,像是 1
是粗體、3
是斜體、9
是刪除線等等

所以你也可以把他們組起來做出 又斜又粗黃底藍字加刪除線 的 Hello World

因為像這種跳脫字元是通用的,所以不管你寫的是什麼語言都可以用這種方式來上色,像我把上面那串貼到 Node.js 裡面去跑,就會出現漂亮(?)的 Hello World

蛤可是這樣寫起來好醜
雖然說可以這樣幫輸出結果加上顏色,可是程式碼會看起來很醜,不仔細看根本不知道在寫什麼。所以其實各語言都有一些包裝好的 library 可以用,像 JS 就是用 chalk,輕輕鬆鬆就可以上色

如果仔細去看 chalk 的原始碼,會發現他就是把上面那串 \033[ <SGR> m
包裝成一個個 function 讓你方便用而已,所以最後的效果都是一樣的

如果不是寫 JS 的話,像 Go、Rust、Python 也有 color、ansi_term 跟 yachalk 可以用。下面是 Rust 的 ansi_term 提供的寫法,也是輕輕鬆鬆~

而 Rust ansi_term 的實作 也是差不多的方式,就是幫你把這堆醜東西封裝起來,眼不見為淨XD

總結
關於可以用 Escape Sequence 來上色這件事,之前參加鐵人賽的時候就簡單寫過~但那時每天都要寫文章沒時間研究得更深入。這次除了教大家怎麼用 SGR 參數之外,還稍微看了 JS 跟 Rust 的 color library 實作,雖然內容不難,但我自己覺得滿有趣的
雖然今天只跟大家介紹 30 到 37 共八種顏色可以用,但隨著現在越來越多終端機支援 256 色,有興趣的話也可以到 wiki 上面看怎麼使用更多其他的顏色,就可以讓終端機的顏色更豐富哦~