有很多方法可以为终端和外壳环境着色。单个命令的输出(例如ls和grep)也可以着色。在控制台上播放媒体的概念虽然没有直接关系但很有趣,但这似乎依赖于窗口系统之上的一些框架(库)。以下问题仅针对bashshell 及其在 Linux 终端框架中的实现及其基础。
请考虑以下 2D游戏场景的 ASCII“渲染”蒙太奇:

这些不是随机生成的场景。我选择的所有片段实际上都描绘了某种形式的“草原”地形(树木、灌木和灌木、花、草等),来自使用 ASCII 字符表示此类对象的游戏。最后 4 个场景展示了用户制作的图块集,它们基本上是带有颜色规格的 ASCII 字符的重新映射(这些细节是微不足道的 - 可以说这是我在这里尝试在视觉效果方面实现的视觉灵感,“图案”)。
蒙太奇分享的那些场景的共同特点是:
我目前在 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
Run Code Online (Sandbox Code Playgroud)
我仍然没有从概念上理解我在使用控制台时没有输入的那些空/空白/背景“空间”是什么,即“它们是由什么组成的?” 可以这么说。特别是那些不在提示符下的,并且环绕回显的命令的那些。关于活动行上发生的事情,可以证明它bash以“面向行”的方式起作用,并且某些操作会触发活动行的清除(for i in $(seq 1 $(expr $(tput lines) \* $(tput cols))); do echo -n M; done; tput cup 15 1,然后在提示符下键入一个字符并退格它 - 演示贡献者) - 其范围可能因 CLI 到另一个(即 zsh)而异。此外,似乎当我\[\033[44m\]在我的 PS1 行中添加类似的东西时,我bash.bashrc在重新加载 bash 后得到了蓝色背景 - 所以显然我知道有一些就背景而言,在此利用输出外观。
但我也知道 bash 是一种软件,它依赖于TTY 子系统形式的其他一些工具来将东西带到屏幕上 - 这从那里到我假设的内核中的VT 组件。pstree -Ap在 Arch 上显示systemd链接到login,然后链接到bash。
在Arch Linux的发行依靠agetty的TTY服务。一个简单的echo $TERM将产生正在使用的终端类型(此处为任何 DE 之外的“linux”),并且infocmp[-d spec1 spec2]不带参数的命令显示来自terminfo(5) 终端数据库的活动终端功能和配置文件信息:
# 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,
Run Code Online (Sandbox Code Playgroud)
就目前而言,终端框架可以利用许多功能,并且基本上是那些在 bash.bashrc 配置文件中公开的功能,因为提示是通过设置 PS1 变量自定义的。控制和转义序列基本上用于中断终端中字符显示的流程,以提供功能,包括移动光标和终端信息数据库中描述的其他功能。其中许多函数是使用众所周知的ESC[(或 \33)控制序列引入器(更多序列here和here,以及一些示例)传入的。此外,还可以使用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
Run Code Online (Sandbox Code Playgroud)
这为以下问题提供了上下文:终端中的空白空间/背景颜色是否可以替换为随机(但漂亮)的 ASCII 字符集?但不知道如何实现这些功能或我在终端中寻找什么。
因此,我创建了一个粗略的模型作为示例,说明如果可能的话,最终结果会是什么样子(不是认真的:):
基本上终端中的所有“空白空间”都将填充图案(这里我“平铺”了上面的一个图像,但我希望在实际实现中从一组随机生成每个单独的“空白”从将指定的蒙太奇记录的 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
Run Code Online (Sandbox Code Playgroud)

观察
head -c 1以行tput cols相乘开始,这将从引用的选择中打印单个随机字符 - 因为它太慢了。我认为不会random生成 (tput cols) 长整数,但它仍然更快。当然,这一切都非常浪费,但它有效。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...
Run Code Online (Sandbox Code Playgroud)
我会将值通过管道传输到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
Run Code Online (Sandbox Code Playgroud)
总之,我正在使用 P_C 来尝试实现持久的视觉模式,即添加了 2 行。不幸的是,我无法在重复我的“水”技巧的同时成功创建这两种模式,即具有蓝色的活动线(这只是改变背景颜色,做一条清晰的线,然后将背景改回黑色)。我拼凑了一张图片来展示这是如何一起播放的:

观察
lsbash。$(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\]"
Run Code Online (Sandbox Code Playgroud)
使用 PROMPT_COMMAND 所做的是每次在生成提示之前打印 3 行模式 - 这 3 组模式是在 0.2 中解释的约束范围内单独生成的 - 对水毫无意义,因为它是 1 个字符,但仍然如此。然后我们往上走两行(使用tput cuu 2),中间一行根据PS1生成提示。我们仍然拥有 .bashrc 加载上的全屏模式的初始命令集,当我们登录到终端时,它只执行一次。现在我们在活动行周围有一些填充,它有自己的蓝色图案,当有回车时总是重复。PS1 变量和 P_C 的内容已被清理。嵌入在 long 中的转义序列和颜色编码的语法echo序列可能很棘手。错误导致奇怪的终端行为包括相互覆盖的行、出现在远离左边距的提示或对无意处理的内容的异常输出。我正在做的事情存在一个条件,其中 PS1 变量内需要额外的空间来抵消 linux 终端和 lxterm 与我的设置(Arch Bang)之间的视觉差异。如果没有额外的空间,linux 终端会在最后一行的末尾打印提示的第一个字符,原因我无法弄清楚(当然,这是我做的事情,而不是默认行为)。也无法弄清楚如何对引号中的字符集生成一些随机效果(粗体、反转等),因为很早就决定生成更长的字符串以提高性能。
终端打开时的初始模式

aclear并在提示符下连续按 Enter后的行为

观察
小智 7
与问题一起提供的早期实现依赖于使用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)"
Run Code Online (Sandbox Code Playgroud)
此实现不再渲染每行,而是在sed处理结束时一次性打印整个序列。这仅在登录一次或通常在bash启动时出现。这是启动时的一种随机模式(我们可以看到两种深浅的绿色和两种深浅的青色):

屏幕显示标准 linux 终端中的结果,它也适用于 xterm。我在 PS1 提示符中使用了一些新的模式字符,而 PROMPT_COMMAND 只处理活动行及其使用 1 字节字符的 2 行填充。
该模式也很好地匹配了我当前archbey在 .bashrc中调用的发行版:

快到圣诞节了!干杯人:)
| 归档时间: |
|
| 查看次数: |
3890 次 |
| 最近记录: |