習(xí)慣于使用Linux的人,時(shí)常需要在終端命令行工作,默認(rèn)的黑白界面看的蒼白而單調(diào)。實(shí)際上我們可以美化它的顯示,之前蟲(chóng)蟲(chóng)有很多文章中曾介紹過(guò)很多這樣的工具和小APP,大家可以我的參考?xì)v史文章參考學(xué)習(xí)。除了這些工具外有沒(méi)有其他辦法美化命令行呢,還有如何在我們自己的腳本中呈現(xiàn)色彩化的顯示呢?本文蟲(chóng)蟲(chóng)就來(lái)教你實(shí)現(xiàn)這些,包括文字著色,自由的定位移動(dòng)光標(biāo)(向上,向下,向左或向右),清屏重顯示(實(shí)現(xiàn)動(dòng)態(tài)進(jìn)度條,ASCII動(dòng)畫(huà))。這其實(shí)都是使用ANSI標(biāo)準(zhǔn)轉(zhuǎn)義碼編程實(shí)現(xiàn)的,今天蟲(chóng)蟲(chóng)就給大家來(lái)說(shuō)明ANSI轉(zhuǎn)義碼的使用方法,最后還使用Python語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單命令行界面。
在*nix體系下數(shù)程序與終端交互的方式是通過(guò)ANSI轉(zhuǎn)義碼。這些轉(zhuǎn)義碼是作為程序可打印的特殊代碼,以便擴(kuò)展終端的顯示。當(dāng)然由于兼容性問(wèn)題,各種終端對(duì)ANSI轉(zhuǎn)義碼支持也有差異,但是基本上在Linux(Windows可能解析有問(wèn)題)下基本的ANSI轉(zhuǎn)義代碼還兼容的存在。首先舉個(gè)例子"Hello,Chongchong"開(kāi)始:
最基本的ANSI轉(zhuǎn)義碼對(duì)文本進(jìn)行渲染的代碼。它允許我們給要打印的文本添加顏色、背景顏色或其他裝飾:
最基本操作是對(duì)文本著色。ANSI顏色轉(zhuǎn)義碼:
紅色:\u001b[31m
重置:\u001b[0m
這個(gè)\u001b前綴是大多數(shù)ANSI顏色轉(zhuǎn)義碼的開(kāi)頭。大多數(shù)編程語(yǔ)言都支持用這種語(yǔ)法來(lái)表示特殊字符,比如Java,Perl,Python和Javascript等都支持\u001b語(yǔ)法。
例如,這里打印字符串"Hello World",但是紅色:
print("\u001b[31mHello ,chongchong\n")
上面顯示了打印了紅色的Hello chongchong,連提示符都變成紅色的了。事實(shí)上,此后再輸入的代碼都將顯示為紅色。這就是Ansi顏色轉(zhuǎn)義碼渲染的工作方式,設(shè)置打印特殊的顏色代碼后,設(shè)置就會(huì)一直生生效,除非再設(shè)置顏色代碼或者用Reset的代碼來(lái)恢復(fù)到默認(rèn)顏色。比如我們打印下Reset的代碼:
可以看到提示符號(hào)恢復(fù)到了回白色。在我們的代碼中如果我們?cè)O(shè)置過(guò)顏色代碼,一定要接著最后用Rest恢復(fù)初始環(huán)境,對(duì)上面的例子我們改造一下:
上面我講了顏色代碼使用和恢復(fù),終端共支持8種(代碼0,30-37)不同的顏色,分別為:
重置:\u001b[0m
黑色:\u001b[30m
紅色:\u001b[31m
綠色:\u001b[32m
黃色:\u001b[33m
藍(lán)色:\u001b[34m
洋紅:\u001b[35m
青色:\u001b[36m
白特:\u001b[37m
我們用上面的顏色打印下字母:
大多數(shù)終端除了基本的8種顏色外,還支持"明亮"顏色。這些也都有自己的轉(zhuǎn)義碼,修飾正常的顏色,但在代碼中額外要加一個(gè);1
亮黑: \u001b[30;1m
亮紅: \u001b[31;1m
亮綠: \u001b[32;1m
亮黃: \u001b[33;1m
亮藍(lán): \u001b[34;1m
亮洋紅: \u001b[35;1m
亮青: \u001b[36;1m
亮白: \u001b[37;1m
Reset: \u001b[0m
我們可以打印出這些鮮艷的顏色并看到它們的效果:
可以看到它們確實(shí)比基本的8種顏色更亮。黑色A現(xiàn)在足夠亮,可以在黑色背景上成了灰色可見(jiàn)了,白色的H現(xiàn)在比默認(rèn)的文本顏色更亮。
在16種色的基礎(chǔ)上,有些些終端支持256色的擴(kuò)展的,265中顏色的轉(zhuǎn)義碼格式為:
\u001b[38;5;${ID}m
下面我們寫(xiě)一個(gè)程序打印所有者256種顏色,為了顯示方便,我們使用jupyer notebook在瀏覽器上顯示:
import sys
for i in range(0, 16):
for j in range(0, 16):
code=str(i * 16 + j)
sys.stdout.write(u"\u001b[38;5;" + code + "m " + code.ljust(4))
print("\u001b[0m")
其中,也支持用jupyer notebook在瀏覽器上顯示:
代碼中,我們使用sys.stdout.write,這樣就可以在同一行上打印多個(gè)顏色,
ANSI轉(zhuǎn)義碼也支持設(shè)置文本背景的顏色。
例如,8種背景顏色對(duì)應(yīng)的代碼為(40-47):
黑色背景: \u001b[40m
紅色背景: \u001b[41m
綠色背景: \u001b[42m
黃色背景: \u001b[43m
藍(lán)色背景: \u001b[44m
洋紅背景: \u001b[45m
青色背景: \u001b[46m
黑色背景: \u001b[47m
也支持亮色版本的背景色:
亮黑色背景: \u001b[40;1m
亮紅色背景: \u001b[41;1m
亮綠色背景: \u001b[42;1m
亮黃色背景: \u001b[43;1m
亮藍(lán)色背景: \u001b[44;1m
亮洋紅背景: \u001b[45;1m
亮青色背景: \u001b[46;1m
亮白色背景: \u001b[47;1m
Reset的代碼頁(yè)一樣都為: \u001b[0m也一樣
我們可以將它們打印出來(lái)并看到它們有效:
注意,背景顏色的亮色版不改變背景,而是增加前景文本的亮度,所以顯示效果不是很直觀(guān)。
也用一個(gè)小腳本展示:
import sys
for i in range(0, 16):
for j in range(0, 16):
code=str(i * 16 + j)
sys.stdout.write("\u001b[48;5;" + code + "m " + code.ljust(4))
print("\u001b[0m")
Jupyter顯示:
除了顏色和背景顏色,ANSI轉(zhuǎn)義碼還支持一些文本格式修飾符
粗體:\u001b[1m
下劃線(xiàn):\u001b[4m
反色:\u001b[7m
每一個(gè)都可以單獨(dú)或者組合使用,或者于顏色符組合使用,看下面的例子:
光標(biāo)移動(dòng)的ANSI轉(zhuǎn)義碼更復(fù)雜,它允許我們?cè)诮K端窗移動(dòng)光標(biāo),或者清除部分窗口。其中最基本的是向上,向下,向左或向右的光標(biāo)移動(dòng):
↑: \u001b[{n}A
↓: \u001b[{n}B
→: \u001b[{n}C
←: \u001b[{n}D
為了演示需要,我們添加一個(gè)time.sleep(10),以便觀(guān)察顯示效果。
import time
print("Hello I Am Chongchong"); time.sleep(10
光標(biāo)導(dǎo)航ANSI轉(zhuǎn)義碼可以利用來(lái)做的最簡(jiǎn)單的事情就是進(jìn)度條:
import time, sys
def loading():
print("Loading...")
for i in range(0, 100):
time.sleep(0.1)
sys.stdout.write("\u001b[1000D" + str(i + 1) + "%")
sys.stdout.flush()
print()
loading()
上面我們用ANSI轉(zhuǎn)義碼來(lái)控制終端,創(chuàng)建一個(gè)顯示進(jìn)度百分比,我們對(duì)其進(jìn)行一下美化,比如顯示一個(gè)完成進(jìn)度條:
import time, sys
def loading():
print "Loading..."
for i in range(0, 100):
time.sleep(0.1)
width=(i + 1) / 4
bar="[" + "#" * width + " " * (25 - width) + "]"
sys.stdout.write(u"\u001b[1000D" + bar)
sys.stdout.flush()
loading()
代碼原理:利用循環(huán)迭代,刪除整行顯示,然后重新繪制一個(gè)更長(zhǎng)的顯示,從而實(shí)現(xiàn)一個(gè)動(dòng)態(tài)ASCII進(jìn)度條。效果如下:
利用向上和向下光標(biāo)代碼移動(dòng),我們甚至可以一次性繪多個(gè)進(jìn)度條,比如下面代碼我們并行三個(gè)進(jìn)度條顯示:
import time, sys, random
def loading(count):
all_progress=[0] * count
sys.stdout.write("\n" * count)
while any(x < 100 for x in all_progress):
time.sleep(0.01)
unfinished=[(i, v) for (i, v) in enumerate(all_progress) if v < 100]
index, _=random.choice(unfinished)
all_progress[index] +=1
sys.stdout.write("\u001b[1000D")
sys.stdout.write("\u001b[" + str(count) + "A")
for progress in all_progress:
width=progress / 4
print("[" + "#" * width + " " * (25 - width) + "]")
loading()
代碼原理:
為了確保有足夠的空間來(lái)繪制進(jìn)度條,我們通過(guò)在函數(shù)啟動(dòng)時(shí)寫(xiě)入"\n" * count來(lái)完成的。該代碼會(huì)創(chuàng)建一列新行,使終端滾動(dòng),確保在終端底部有準(zhǔn)確的空白行,以便呈現(xiàn)進(jìn)度條;使用all_progress數(shù)組模擬正在進(jìn)行的多項(xiàng)操作,并使該數(shù)組被隨機(jī)填充使用Up ANSI代碼每次移動(dòng)光標(biāo)計(jì)數(shù)行,這樣我們就可以每行打印一個(gè)計(jì)數(shù)進(jìn)度條。
使用ANSI轉(zhuǎn)義碼可以做的更有意義事情之一就是實(shí)現(xiàn)一個(gè)命令行界面。Bash,Python,Ruby都有自己的內(nèi)置命令行,編輯文本,提交命令,解析執(zhí)行。上面我們學(xué)習(xí)了,如何使用ANSI,本部分我們利用這些命轉(zhuǎn)義碼實(shí)現(xiàn)自己的命令行界面。
命令界面最重要的一部分就是用戶(hù)接口,負(fù)責(zé)接受用戶(hù)輸入。這可以使用以下代碼完成:
import sys, tty
def command_line():
tty.setraw(sys.stdin)
while True:
char=sys.stdin.read(1)
if ord(char)==3:
break;
print(ord(char))
sys.stdout.write(u"\u001b[1000D")
上面代碼中,我們使用setraw來(lái)確保我們的原始字符輸入直接被程序接收,然后讀取并顯示我們的按鍵的ASCII碼,如果按下3(CTRL-C的代碼)。由于我們已打開(kāi)tty.setraw打印,不會(huì)進(jìn)行光標(biāo)重置,最后我們執(zhí)行向左移動(dòng)\u001b[1000D移動(dòng)光標(biāo)到最左。顯示效果如下:
基于上面的基本接口,我們來(lái)實(shí)現(xiàn)一個(gè)原始命令行界面,用來(lái)回顯用戶(hù)鍵入的內(nèi)容,我們約定:
當(dāng)用戶(hù)按下可打印字符時(shí),直接打印出來(lái)
當(dāng)用戶(hù)按Enter鍵時(shí),打印出用戶(hù)輸入,換行輸入。
當(dāng)用戶(hù)按Backspace鍵時(shí),刪除光標(biāo)所在的一個(gè)字符
當(dāng)用戶(hù)按下箭頭鍵時(shí),使用ANSI轉(zhuǎn)義碼向左或向右移動(dòng)光標(biāo)。
首先,讓我們首先實(shí)現(xiàn)前兩個(gè)功能,代碼如下:
import sys, tty
def command_line():
tty.setraw(sys.stdin)
while True:
input=""
while True:
char=ord(sys.stdin.read(1))
if char==3:
return
elif 32 <=char <=126:
input=input + chr(char)
elif char in {10, 13}:
sys.stdout.write(u"\u001b[1000D")
print("\nechoing..."), input
input=""
sys.stdout.write(u"\u001b[1000D")
sys.stdout.write(input)
sys.stdout.flush()
顯示效果如下:
下一步是讓用戶(hù)使用箭頭鍵移動(dòng)光標(biāo)。鍵盤(pán)上左右箭頭鍵對(duì)應(yīng)于字符碼27 91 68和27 91 67的序列,所以我們可以對(duì)輸入代碼檢查并對(duì)應(yīng)移動(dòng)光標(biāo)
代碼原理:
通過(guò)維護(hù)一個(gè)索引變量,保留一個(gè)單獨(dú)的索引,該索引不一定在輸入的末尾,當(dāng)用戶(hù)輸入一個(gè)字符時(shí),將其拼接到輸入的正確位置。
檢查char==27,然后檢查接下來(lái)的兩個(gè)字符來(lái)識(shí)別左右箭頭鍵,并遞增/遞減光標(biāo)的索引。
寫(xiě)入輸入后,手動(dòng)將光標(biāo)一直向左移動(dòng),并向右移動(dòng)與光標(biāo)索引對(duì)應(yīng)的正確字符數(shù)。效果如下:
后續(xù)根據(jù)需要,可以增加Home和End(^和$)功能,只需通過(guò)類(lèi)似方法添加代碼即可,我們不在多贅述。
我們還要實(shí)現(xiàn)刪除功能:使用Backspace將會(huì)刪除光標(biāo)前字符,并將光標(biāo)向左移動(dòng)一位。為了實(shí)現(xiàn)效果我們還得用一個(gè)ANSI轉(zhuǎn)義碼實(shí)現(xiàn)各種清屏工作:
清除屏幕:
\u001b[{n}J清除屏幕
n=0從光標(biāo)清除到屏幕結(jié)束,
n=1從光標(biāo)到屏幕的開(kāi)頭清除
n=2清除整個(gè)屏幕
清除行:
\u001b[{n}K清除當(dāng)前行
n=0從光標(biāo)到行尾清除
n=1從光標(biāo)到行首開(kāi)始清除
n=2清除整行
下面的代碼:
sys.stdout.write(u"\u001b[0K")
清除光標(biāo)位置到行末的所有字符。
增加刪除功能后的代碼:
效果如下:
工作原理如下:
光標(biāo)移動(dòng)到行的開(kāi)頭sys.stdout.write(u"\u001b[1000D")
清除行sys.stdout.write(u"\u001b[0K")
當(dāng)前輸入寫(xiě)入sys.stdout.write(輸入)
光標(biāo)移動(dòng)到正確索引的位置sys.stdout.write(u"\u001b[" + str(index) + "C")
通常,使用這些代碼時(shí)候,都會(huì)在調(diào)用.flush()時(shí)生效。
最終我們實(shí)現(xiàn)了了一個(gè)最小規(guī)格的命令行界面,使用sys.stdin.read和sys.stdout.write實(shí)現(xiàn)讀寫(xiě),用ANSI轉(zhuǎn)義碼控制終端。
截止目前,我們已經(jīng)嘗試使用ANSI轉(zhuǎn)義符顯示顏色,光標(biāo)導(dǎo)航實(shí)現(xiàn)進(jìn)度條,并實(shí)現(xiàn)了一個(gè)原始的命令行界面。最后我們給我們的命令行界面增加一個(gè)功能,對(duì)其中代碼實(shí)現(xiàn)語(yǔ)法高亮。
基于我們以后的代碼,實(shí)現(xiàn)語(yǔ)法高亮就像在輸入字符串上調(diào)用一個(gè)syntax_highlight函數(shù)一樣簡(jiǎn)單,
sys.stdout.write(syntax_highlight(input))
為了演示我將使用一個(gè)虛擬語(yǔ)法高亮顯示器來(lái)突出顯示尾隨空格。
def syntax_highlight(input):
stripped=input.rstrip()
return stripped + u"\u001b[41m" + " " * (len(input) - len(stripped)) + u"\u001b[0m"
這就是一個(gè)最簡(jiǎn)單的實(shí)例,為了支持更強(qiáng)大的功能,可以利用Pygments的類(lèi)庫(kù)來(lái)替換上面syntax_highlight函數(shù),這樣就可以實(shí)現(xiàn)任何編程語(yǔ)言的真正的語(yǔ)法高亮。
這種與命令行程序的"豐富"交互是大多數(shù)傳統(tǒng)命令行程序和庫(kù)所缺乏的。在充分了解ANSI轉(zhuǎn)義碼的基礎(chǔ)上實(shí)現(xiàn)自己的富終端明亮行界面并不像想象的那么難。
平時(shí)使用命令行工具時(shí),是不是老是覺(jué)得界面單調(diào)乏味?
如果是的話(huà),今天你必須看完TJ君這篇推薦,TJ君今天教你如何用上最時(shí)尚潮流好看的命令行!
StarShip,一個(gè)適用于任何 Shell、任何操作系統(tǒng)定制化高顏值終端!
首先,讓自己變得好看的前提是準(zhǔn)備好Nerd Font字體。
Nerd Font是一種專(zhuān)為開(kāi)發(fā)人員準(zhǔn)備的,各種大量圖標(biāo)或者形狀的美妙(夸張???)字體
找好字體并完成安裝之后,我們就要進(jìn)行下一步,安裝StarShip,作者提供了不同系統(tǒng)的安裝方式,例如Linux、Mac、android、windows等等。
Linux
curl -sS https://starship.rs/install.sh | sh
Mac
curl -sS https://starship.rs/install.sh | sh
安裝完之后,需要初始化配置,以windows為例,使用CMD需要配合Clink,在以下文件%LocalAppData%\clink\starship.lua中初始化如下內(nèi)容:
load(io.popen('starship init cmd'):read("*a"))()
并在C:\Users\用戶(hù)名\.config文件夾下新增配置文件starship.toml,里面的初始內(nèi)容可以是:
# 根據(jù) schema 提供自動(dòng)補(bǔ)全
"$schema"='https://starship.rs/config-schema.json'
# 在提示符之間插入空行
add_newline=true
# 將提示符中的 '?' 替換為 '?'
[character] # 此組件名稱(chēng)為 'character'
success_symbol='[?](bold green)' # 將 'success_symbol' 字段設(shè)置成顏色為 'bold green' 的 '?'
# 禁用 'package' 組件,將其隱藏
[package]
disabled=true
toml配置文件弄好之后,記得要在starship.lua增加: os.setenv('STARSHIP_CONFIG', 'C:\Users\user\example\non\default\path\starship.toml')
重啟自己的命令行工具,就是這樣的效果了:
官方論壇上還有其他人的實(shí)際效果,看著都不錯(cuò)呢!難怪能有40K+的Star!
想讓自己也變得好看的小伙伴,趕緊來(lái)試試吧
GitHub地址:https://github.com/starship/starship
友情鏈接: 餐飲加盟
地址:北京市海淀區(qū) 電話(huà):010- 郵箱:@126.com
備案號(hào):冀ICP備2024067069號(hào)-3 北京科技有限公司版權(quán)所有