Bash

終端中的空格/背景可以用隨機(但漂亮)的 ASCII 字元模式替換嗎?

  • June 24, 2019

背景和問題

有很多方法可以為終端和 shell 環境著色。單個命令的輸出,例如lsgrep,也可以著色。沒有直接相關但有趣的是在控制台上播放媒體的概念,但這似乎依賴於視窗系統之上的一些框架(庫)。以下問題僅針對bashshell 及其在 Linux 終端框架中的實現及其基礎。

請考慮以下 2D遊戲中場景的 ASCII“渲染”蒙太奇:

Dwarf Fortress 遊戲中的自然場景(樹木、灌木、花朵、草) - 前 2 個螢幕顯示沒有圖塊集的遊戲(僅 ASCII),第三個螢幕使用 Vherid 圖塊集生成,接下來的 2 個螢幕使用CLA 瓦片集,最後一個螢幕是使用 Jolly Bastion 瓦片集生成的

這些不是隨機生成的場景。我選擇的片段實際上都從使用 ASCII 字元表示此類對象的遊戲中描繪了某種形式的“草原”地形(樹木、灌木和灌木、花、草等)。最後 4 個場景展示了使用者製作的圖塊集,它們基本上是帶有顏色規格的 ASCII 字元的重新映射(這些細節是微不足道的 - 可以說這是我在這裡嘗試在視覺方面完成的視覺靈感和“圖案”)。

蒙太奇中的這些場景共有的共同特徵是:

  • 最多 5-6 個不同的 ASCII 字元(逗號、引號和其他一些字元)

  • 使用2-4種顏色

    • 對於字元
    • 對於某些情況下的字元背景 - 最後一個範例顯示使用很少或沒有字元的顏色陰影來創建圖案,即彩色馬賽克

我目前在 VM 中擁有的是Arch Linux,儘管問題不是特定於發行版的,但我已經查看了他們的文件以自定義/etc/bash.bashrc文件。我可以看到很多解釋都涉及到配置提示的外觀以及通常所有的前景元素。關於背景的任何配置的資訊很少,除了通常是純色,例如這些設置和提示

# Background
On_Black='\e[40m'       # Black
On_Red='\e[41m'         # Red
On_Green='\e[42m'       # Green
On_Yellow='\e[43m'      # Yellow
On_Blue='\e[44m'        # Blue
On_Purple='\e[45m'      # Purple
On_Cyan='\e[46m'        # Cyan
On_White='\e[47m'       # White

我仍然不明白我在使用控制台時沒有輸入的那些空白/空白/背景“空格”是什麼,即“它們是由什麼製成的?” 可以這麼說。尤其是那些不在提示符下的,並且環繞著回顯的命令。關於活動行上發生的事情,可以證明bash以“面向行”的方式進行操作,並且某些操作會觸發活動行的清除 ( for i in $(seq 1 $(expr $(tput lines) \* $(tput cols))); do echo -n M; done; tput cup 15 1,然後在提示符處鍵入一個字元並退格 - 展示一個貢獻者) - 其範圍可能從 CLI 到另一個 CLI 不同,即 zsh。此外,似乎當我\[\033[44m\]在我的 PS1 行中添加類似的東西時,我bash.bashrc在重新載入 bash 後得到了藍色背景 - 所以顯然我知道有一些**就背景而言,在這裡利用輸出外觀。

但我也知道 bash 是一個軟體,它依賴於TTY 子系統形式的其他一些工具,用於將內容顯示在螢幕上——這從那裡到我假設的核心中的VT 組件pstree -Ap在 Arch 顯示systemd連結到login然後到bash.

Arch Linux發行版依賴於agettyTTY 服務。一個簡單echo $TERM的將產生正在使用的終端類型(此處為“linux”,在任何 DE 之外),不帶參數的命令顯示來自terminfo(5) 終端數據庫infocmp[-d spec1 spec2]的活動終端功能和配置文件資訊:

# Reconstructed via infocmp from file: /usr/share/terminfo/l/linux
linux|linux console,
am, bce, ccc, eo, mir, msgr, xenl, xon,
colors#8, it#8, ncv#18, pairs#64,
acsc=+\020\,\021-\030.^Y0\333'\004a\261f\370g\361h\260i\316j\331k\277l\332m\300n\305o~p\304q\304r\304s_t\303u\264v\301w\302x\263y\363z\362{\343|\330}\234~\376,
bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l\E[?1c,
clear=\E[H\E[J, cnorm=\E[?25h\E[?0c, cr=^M,
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C,
cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
cvvis=\E[?25h\E[?8c, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m,
dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K,
el1=\E[1K, flash=\E[?5h\E[?5l$, home=\E[H,
hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, ich1=\E[@,
il=\E[%p1%dL, il1=\E[L, ind=^J,
initc=\E]P%p1%x%p2%{255}%*%{1000}%/%02x%p3%{255}%*%{1000}%/%02x%p4%{255}%*%{1000}%/%02x,
kb2=\E[G, kbs=\177, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B,
kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[3~, kend=\E[4~, kf1=\E[[A,
kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~,
kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~,
kf18=\E[32~, kf19=\E[33~, kf2=\E[[B, kf20=\E[34~,
kf3=\E[[C, kf4=\E[[D, kf5=\E[[E, kf6=\E[17~, kf7=\E[18~,
kf8=\E[19~, kf9=\E[20~, khome=\E[1~, kich1=\E[2~,
kmous=\E[M, knp=\E[6~, kpp=\E[5~, kspd=^Z, nel=^M^J, oc=\E]R,
op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rmacs=\E[10m,
rmam=\E[?7l, rmir=\E[4l, rmpch=\E[10m, rmso=\E[27m,
rmul=\E[24m, rs1=\Ec\E]R, sc=\E7, setab=\E[4%p1%dm,
setaf=\E[3%p1%dm,
sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m,
sgr0=\E[0;10m, smacs=\E[11m, smam=\E[?7h, smir=\E[4h,
smpch=\E[11m, smso=\E[7m, smul=\E[4m, tbc=\E[3g,
u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?6c, u9=\E[c,
vpa=\E[%i%p1%dd,

就目前而言,可以從終端框架中利用許多功能,並且基本上是那些在 bash.bashrc 配置文件中公開的功能,只要通過設置 PS1 變數來自定義提示。控制和轉義序列用於基本上中斷終端中字元顯示的流程,以提供功能,包括移動游標和終端資訊數據庫中描述的其他功能。其中許多函式是使用眾所周知的ESC[(或\33)控制序列引入器(更多序列herehere,以及一些範例)傳入的。此外,還可以使用tput直接在 CLI 上的實用程序來更改一些終端屬性;例如tput setab 4將在藍色背景上有 bash echo 命令。

如果我們strace bash可以同時看到轉義序列和行為:

write(2, "[il@Arch64vm1 ~]$ ", 19[il@Arch64vm1 ~]$ ) = 19  //bash starts
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0, " ", 1) = 1                                 //pressed <space>
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
write(2, " ", 1 ) = 1
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0, "\177", 1) = 1                              //pressed <backspace>...
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
write(2, "\10\33[K", ) = 4                          //triggers erasing the line
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0, "\33", 1) = 1                               //pressed <esc> per se

這為問題*可以用隨機(但漂亮)的 ASCII 字元集替換終端中的空格/背景顏色嗎?*但不知道如何實現這些功能或我在終端中尋找什麼。

因此,我創建了一個粗略的模型作為範例,說明如果可能的話,最終結果會是什麼樣子(不認真:):

顯示一組彩色 ASCII 字元作為背景的終端模型,而不僅僅是純黑色。 該模式環繞已執行命令的輸出。 活動命令行具有不同的模式/顏色,但 CLI 永遠不會解釋此模式以保留 shell 的全部功能

基本上,終端中的所有“空白空間”都將被模式填充(這裡我“平鋪”上面的圖像之一,但我希望在實際實現中從一組隨機生成的每個“空白”從蒙太奇記錄的 5-6 個字元和特徵將被指定)。活動命令行有不同的模式,即波浪狀的“水”,但我會接受藍色的線。正如想像的那樣,當在活動行上鍵入命令時,命令將“擦除”“水”,當然,一個約束是字元模式永遠不會被 CLI 解釋,否則它將使其無用。

那麼是否有任何配置暴露在終端框架中bash或終端框架中,或者允許使用一組字元和一些顏色控制來修改終端中 bash 的輸出以便為背景生成某種隨機模式的腳本(這與我上面顯示的類似)?還是我應該簡單地滿足於嘗試提供完整的圖案圖像作為tty 的背景

實現

0.1 - PatternOTD 版本(登錄時一槍交易)

我添加到我的 .bashrc 文件中的以下表達式匯集了我們探索的一些概念,並構成了標準 linux 終端中視覺效果的(非常)基本概念證明:

for i in $(seq 1 $(expr $(tput lines))); do echo -en '\E[32;32m'$(tr -dc '",.;:~' < /dev/urandom | head -c $(tput cols)); done; tput cup 15; tput setab 4; echo -en "\E[2K"; tput setab 0

在此處輸入圖像描述

觀察

  • 它顯然只是一個命令,所以不是持久的,即它會隨著命令的輸入而滾動
  • 選擇不單獨隨機化字元的選擇,即 head -c 1tput cols開始的行相乘,這將從引用的選擇中列印單個隨機字元 - 因為它太慢了。我認為不會random生成 (tput cols) 長整數,但它仍然更快。當然,這一切都非常浪費,但它確實有效。
  • 除了綠色之外,沒有隨機化每個字元的任何顏色或效果,因為正如我解釋的那樣,單獨渲染/處理每個字元太慢了。回复:幀緩衝?
  • 我很高興看到該模式不會干擾 CLI 的使用,因為它不會被 CLI 解釋!(為什麼雖然我無法解釋)
  • 水流得太快了!;-)

0.2 - PROMPT_COMMAND 破解作業

在 Bash 列印每個主要提示之前檢查變數PROMPT_COMMAND的值。我知道通常您會使用該變數來呼叫腳本,您可以在其中處理來自顯示等的元素,但我寧願嘗試直接在我的 .bashrc 文件中執行此操作。最初我認為我可以實現一些位置感知,即執行前游標在哪裡(所以我可以在螢幕上的任何地方渲染東西,tput然後回到我之前的位置,使用類似這樣的東西來提取位置:

stty -echo; echo -n $'\e[6n'; read -d R x; stty echo; echo ${x#??}  //value is in x;x format so...

我會將值通過管道傳輸到cut -f1 -d";". 我可以在 CLI 上執行此操作,但目前無法在 PS1/P_C 變數中的元素序列中執行此操作,並且可能不會在每次輸入時評估放入 PROMPT_COMMAND 的任何命令,而是只有一次(?),儘管每次都被執行(見下面的觀察)。

所以我能做的最好的就是延續我的初始序列,並在 PROMPT_COMMAND 和 .bashrc 中 PS1 變數的定義中添加一些命令。像這樣:

PROMPT_COMMAND="echo -en '\E[32;32m'$(tr -dc ',.:~' < /dev/urandom | head -c $(echo "$[$(tput cols) * 2]"))"

PS1="$(echo -en '\n') $(tput setab 4)$(echo -en "\E[2K")$(tput setab 0)\[\033[7;32m\]df:\[\033[1;34m\] \W @d \[\033[0m\]\e[32m"

for i in $(seq 1 $(expr $(tput lines))); do echo -en '\E[32;32m'$(tr -dc '",.;:~' < /dev/urandom | head -c $(tput cols)); done; tput cup 1; tput setab 4; echo -en "\E[2K"; tput setab 0

總之,我正在使用 P_C 來嘗試實現持久的視覺模式,即添加了 2 行。不幸的是,在重複我的“水”技巧時,我無法成功地創建這兩種模式,即使活動線變為藍色(這只是改變背景顏色,做一條清晰的線條,然後將背景改回黑色)。我整理了一張圖片來展示它是如何一起發揮作用的:

在此處輸入圖像描述

觀察

  • 在一行上使用退格仍然會觸發清除行行為並且藍色消失了
  • 每次按下輸入鍵時,我們在新的活動行之前有 2 行模式
  • 當然,儘管有額外的行,但我們在下面看到的更遠,我們並沒有將模式包裝在命令的一側,例如ls
  • /dev/urandom 的隨機性在 P_C 中呼叫時似乎不是那麼隨機。此圖像由 2 個圖像組成,但很容易發現額外的 2 行模式始終相同,即隨機性不是每次按 enter 鍵時生成的,而是兩行中的每一行僅生成一次 - 可能只有第一行時間 .bashrc 被 .bashrc 讀取bash
  • PS1 變數的內容以$(echo -en '\n') $(tput setab 4)- 中間的那個空間開始,就在 $(tput …) 之前,它必須在那裡才能工作。否則藍線出現在提示頂部而不是在它前面,我無法解決這個問題。而這個 hack 就是 0.2 的名字。:)

0.3 - tput cuu&tput cud

for i in $(seq 1 $(expr $(tput lines))); do echo -en '\E[0;32m'$(tr -dc '",.o;:~' < /dev/urandom | head -c $(tput cols)); done; tput cup 1

PROMPT_COMMAND="echo -en '\033[0;32m$(tr -dc ',;o.:~' < /dev/urandom | head -c $(tput cols))\n\033[36;44m$(tr -dc '~' < /dev/urandom | head -c $(tput cols))\033[0;32m$(tr -dc ',.o+;:~' < /dev/urandom | head -c $(tput cols))'$(tput cuu 2)"

PS1="\[\033[0m\] \[\033[1;32m\][1]\[\033[7;32m\]=2=:\W)\[\033[0;32m\]=3=\[\033[1;32m\]=4=@>\[\033[0;32m\]"

PROMPT_COMMAND 的作用是在生成提示之前每次列印 3 行模式 - 並且這 3 組模式是在 0.2 中解釋的約束內單獨生成的 - 對於水來說沒有意義,因為它是 1 個字元但仍然如此。然後我們往上走兩行(使用tput cuu 2),根據 PS1 在中行生成提示。我們仍然有我們在 .bashrc 載入時全屏模式的初始命令集,當我們登錄到終端時它只執行一次。現在我們在活動行周圍有一些填充,它有自己的藍色圖案,當有輸入時總是重複出現。PS1 變數和 P_C 的內容已經過清理。轉義序列的語法和嵌入在 long 中的顏色編碼echo序列可能很棘手。錯誤導致奇怪的終端行為包括相互覆蓋的行、遠離左邊距的提示或無意中處理的內容的異常輸出。我正在做的事情存在一個條件,其中 PS1 變數內部需要一個額外的空間來抵消我的設置(Arch Bang)在 linux 終端和 lxterm 之間的視覺差異。如果沒有額外的空間,由於某種我無法弄清楚的原因,linux終端會在最後一行的末尾列印提示的第一個字元(當然這是我做的事情,而不是預設行為)。也無法弄清楚如何為引號中的字元集生成一些隨機效果(粗體、反轉等),因為很早就決定生成更長的字元串以提高性能。

終端打開時的初始模式 在此處輸入圖像描述

clear在提示符下連續按輸入後的行為 在此處輸入圖像描述

觀察

  • 應該重新設計或修改以實現對圖案進行著色而不是批量進行
  • 開始覺得要走得更遠,要麼將所有內容放入腳本中,要麼利用某種更高形式的抽象。但是終端功能非常適合最終使用者(讓我想起了“徽標”)!

0.5a - 青風滾草平原

該問題提供的早期實現依賴於使用的命令序列tr和一組單字節字元。正如本問答中所解釋的,該實用程序無法處理多字節字元,例如 Unicode。但是利用這些角色對於達到預期的效果非常重要。提供了一個“解決方案”,它允許在單個流中混合單字節和多字節字元以進行渲染。在那裡開發的解決方案在這裡展示和定制:

Z1=$(echo -en '\xe2\x97\x98') #◘ 1
Z2=$(echo -en '\xe2\x95\x9a') #╚ 2
Z3=$(echo -en '\xe2\x95\x9c') #╜ 3
Z4=$(echo -en '\xe2\x95\x9d') #╝ 4
Z5=$(echo -en '\xe2\x95\x9e') #╞ 5
Z6=$(echo -en '\xe2\x95\x9f') #╟ 6
Z7=$(echo -en '\xe2\x96\x91') #░ 7
Z8=$(echo -en '\xe2\x96\x92') #▒ 8
Z9=$(echo -en '\xe2\x96\x93') #▓ 9
N1=$(echo -en '\xe2\x94\x80') #─ a
N2=$(echo -en '\xe2\x95\x92') #╒ b
N3=$(echo -en '\xe2\x95\x97') #╗ c
N4=$(echo -en '\xe2\x96\xb6') #▶d
N5=$(echo -en '\xe2\x94\xbc') #┼ e
N6=$(echo -en '\xe2\x94\xa4') #┤ f
N7=$(echo -en '\xe2\x95\xa1') #╡ g
Z11="$(tr -dc '123456789a' < /dev/urandom | head -c 1)" //Z11 to Z13 not
Z12="$(tr -dc '123456789a' < /dev/urandom | head -c 1)" // used here (see
Z13="$(tr -dc '123456789a' < /dev/urandom | head -c 1)" //link)

echo -en $(tr -dcs ' ;",15bdef' ' ' < /dev/urandom | head -c $(echo -en "$[$(tput cols) * $(tput lines)]") | sed -e "s/1/$(echo -en "\033[0;36m$Z1\033[0m")/g" -e "s/5/$(echo -en "\033[0;32m$Z5\033[0m")/g" -e "s/b/$(echo -en "\033[1;36m$N2\033[0m")/g" -e "s/d/$(echo -en "\033[1;36m$N4\033[0m")/g" -e "s/e/$(echo -en "\033[0;32m$N5\033[1;32m")/g" -e "s/f/$(echo -en "\033[0;36m$N7\033[1;32m")/g"); tput cup 1
                ^set^+^chars^ to implement from pool - here 1,5,b,d,e,f... so_________________________^add the appropriate sed subprocessing units for implemented chars i.e. first one we replace "1" with the value of $Z1 and apply color at the same time, then all the chars move down the pipe to all required blocks - we selected to implement 6 chars here so we have 6 sed blocks. 

[N.B. To remove the blank space from the pattern, remove it from both sets: tr -dcs ';",15bdef' '']

PS1="\[\033[1;36m\] $(echo -en '\xe2\x96\x91')$(echo -en '\xe2\x96\x92')$(echo -en '\xe2\x96\x93')[\t]$(echo -en '\xe2\x96\x93')$(echo -en '\xe2\x96\x92')$(echo -en '\xe2\x96\x91') \[\033[7;36m\]$(echo -en '\xe2\x97\x98')$(echo -en '\xe2\x94\xbc')$(echo -en '\xe2\x94\x80')\W$(echo -en '\xe2\x94\x80')\[\033[0;36m\]$(echo -en '\xe2\x94\x80')$(echo -en '\xe2\x94\x80')$(echo -en '\xe2\x94\x80')@$(echo -en '\xe2\x96\xb6')\[\033[0;36m\]"

PROMPT_COMMAND="echo -en '\033[0;36m$(tr -dc '=' < /dev/urandom | head -c $(tput cols))\n\033[01;46m$(tr -dc '~' < /dev/urandom | head -c $(tput cols))\033[0;36m$(tr -dc '=' < /dev/urandom | head -c $(tput cols))'$(tput cuu 2)"

此實現不再按行渲染,而是在sed處理結束時一次性列印整個序列。這僅在登錄時出現一次或通常在bash啟動時出現。這是發佈時的一種隨機模式(我們可以看到兩種綠色和兩種青色):

在此處輸入圖像描述

螢幕在標準 linux 終端中顯示結果,它也可以在 xterm 中執行。我在 PS1 提示符中使用了一些新的模式字元,而 PROMPT_COMMAND 只處理活動行及其使用 1 字節字元的 2 行填充。

該模式也與我目前archbey在 .bashrc 中呼叫的分佈非常匹配:

在此處輸入圖像描述

它在聖誕節!乾杯人:)

引用自:https://unix.stackexchange.com/questions/105325