终端中的空白/背景可以替换为ASCII字符的随机(但漂亮)模式吗?


23

上下文和问题

有许多方法可以使终端和外壳环境着色。单个命令(例如ls和)的输出grep也可以被着色。在控制台播放媒体的概念虽然没有直接关系,但很有趣,但这似乎依赖于窗口系统顶部的某些框架(库)。以下问题仅针对bashshell及其在Linux终端框架中的实现及其基础。

请考虑以下2D 游戏中场景的ASCII“渲染”蒙太奇:

Dwarf Fortress游戏中的自然场景(树木,灌木,花卉,草丛)-前两个屏幕显示的游戏中没有图块(仅限ASCII),第三个屏幕是使用Vherid图块生成的,接下来的两个屏幕是使用Vherid图块生成的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,然后在提示符下键入char并退格-演示了一个贡献者)-其范围可能从一个CLI到另一个(即zsh)有所不同。此外,似乎当我在重新加载bash之后\[\033[44m\]在PS1行中添加类似内容时,bash.bashrc背景变为蓝色-很明显,我知道有一些背景而言,此处利用输出外观。

但是我也知道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,

就目前而言,可以从终端框架中利用许多功能,并且只要设置PS1变量自定义提示,就基本上是bash.bashrc配置文件中公开的那些功能。控制和转义序列用于基本中断终端中字符显示的流程,以便提供功能,包括移动光标和终端信息数据库中描述的其他功能。这些功能中的许多功能都是使用众所周知的ESC[(或\ 33)控制序列介绍器传递的(此处此处的更多序列,以及一些示例)。此外,也可以使用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行。不幸的是,在重复我的“水”技巧时(例如,使活动线变为蓝色(这只是改变背景颜色,做一条清晰的线,然后将背景变回黑色)),我无法成功创建这两种图案。我整理了一张图片,展示了它们如何协同工作:

在此处输入图片说明

观察结果

  • 在行上使用退格键仍会触发清除行行为,并且蓝色消失了
  • 每次按下Enter键,我们在新的活动行之前都会有2行图案
  • 当然,尽管有多余的行,但正如我们进一步看到的那样,我们并未将模式包装在命令的侧面,例如 ls
  • 在P_C中调用/ dev / urandom时,其随机性似乎不太随机。该图像由2张图像组成,但是很容易看出,额外的2行图案始终是相同的,即,并不是每次输入Enter键都会产生随机性,而是两行中的每行仅产生一次-可能只有第一行时间.bashrc由读取bash
  • PS1变量的内容以$(echo -en '\n') $(tput setab 4)-中间的空格开始,即$(tput ...)之前,它必须在此处才能起作用。否则,蓝线会出现在提示的顶部而不是提示的前面,我无法解决。而这种hack的名字就是0.2。:)

0.3- tput cuutput 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行图案-并且在0.2中说明的约束内分别生成了这3组图案-对于水来说是毫无意义的,因为它是1个字符,但仍然是水。然后,我们走两行(使用tput cuu 2),并根据PS1在中间行生成提示。对于.bashrc负载,我们仍然具有用于全屏模式的初始命令集,该命令仅在登录到终端时才执行一次。现在我们在活动线周围有一些填充,填充有其自己的蓝色图案,在有回车时总是会重复。PS1变量和P_C的内容已清除。转义序列的语法和嵌入在长字符串中的颜色编码echo序列可能很棘手。错误导致奇怪的终端行为包括彼此覆盖的行,从左侧距出现的提示或不正常的输出到已经无意处理的内容的提示。我正在做的事情存在一个条件,在PS1变量内需要一个额外的空间,以抵消我的设置(Arch Bang)下Linux终端和lxterm之间的视觉差异。没有多余的空间,由于某些我无法弄清楚的原因,Linux终端会在最后一行的末尾打印提示的第一个字符(当然,这是我要做的,而不是默认的行为)。也无法弄清楚如何对引号中的字符集生成某种随机效果(粗体,逆等),因为早期就决定生成更长的字符串以提高性能。

终端打开时的初始模式 在此处输入图片说明

a之后的行为clear并按提示依次按Enter 在此处输入图片说明

观察结果

  • 应该重新设计或修改以实现对图案进行着色,而不是大量进行着色
  • 开始觉得要走得更远,需要将所有内容放到脚本中或利用某种更高形式的抽象。但是终端功能确实为最终用户启用(使我想起了“徽标”)!

20
我有点害怕您要这样做:-)
克里斯·

2
@ Chris Down正如我在完成问询后真的所做的那样,以至于我几乎都没有发布过;)这很特殊,但是我相信答案可以带您深入了解终端中的内容显示方式以及它是否超出了配置和设置范围。是否重新使用该软件。我猜是假期!

3
我也有些害怕,但我认为问没有什么错。我过去做过很多可怕的事情,但是在尝试完成所说的暴行时,我学到了很多东西。再加上一个人认为是坚果,另一个人可能没有。听起来这是一个不错的挑战,请继续努力:-)
Patrick

您是否要在文本模式控制台或帧缓冲区中完成此操作?或者,也许您还想支持xterm?
罗斯兰

@Ruslan也许都是为了完整性?我看到SDL使用了我掌握的帧缓冲区,而我引用的游戏也使用了该框架。另一方面,我的问题是要展示我猜想的终端框架的经典内部结构。当自定义bash并使用bash.bashrc更改前景元素时,就没有求助于帧缓冲区了,因此我对“对文本起作用的东西”或其他实现它的方式也很感兴趣,因为它效率低下或天真是。至于xterm的,以及最初我的意思是不依赖于X的解决方案

Answers:


6

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 中调用的发行版非常匹配:

在此处输入图片说明

圣诞节快到了!欢呼的人:)


1
这不是最终的答案或实现,而是一个里程碑!;-)

也有可能利用aalib之类的库的功能 - aafire例如,aafire -driver curses -extended -gamma 1 -floyd_steinberg -random 5有关热烈的介绍,请参见。

不要错过这个解决这里讨论的utf8编码的unicode限制的答案!!

4

显示由终端管理,该终端与网络浏览器的工作原理相同:它解释字符序列以设置显示(请参阅man terminfo)。bash shell不会告诉终端如何填充屏幕的空白区域。

某些终端能够像gterm一样将图片作为背景,但不是由外壳制作的。


我已经编辑了Q,以快速解释您所指的内容以及隐藏在注释中的其他相关信息。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.