为什么 80 列控制台上有 11 个制表位?

Tan*_*n六四 2 terminal

您会假设,由于默认情况下每个选项卡向前移动 8 列,因此 80 列控制台将恰好有 10 个制表位。这可以反驳:

$ printf "1\t2\t3\t4\t5\t6\t7\t8\t9\t0\ta"
Run Code Online (Sandbox Code Playgroud)

人们会期望输出在 2 行中,因为有 10 个制表位,占用了所有 80 列的空间,加上一个字符,将被推到下一行。但是该命令会在 xterm、gnome-terminal 和 rxvt 中生成单个输出行。

发生的事情是最后一个制表位神奇地减少了一个字符的空间。

同时,expand(1) 维持一个 tab 的空间为 8,因此

$ printf "1\t2\t3\t4\t5\t6\t7\t8\t9\t0\ta" | expand
Run Code Online (Sandbox Code Playgroud)

会在 80 列的控制台上神奇地生成 2 行。

这似乎是某种标准。但我只是无法通过谷歌搜索找到来源。

仅在 Linux 中测试过,但恐怕所有 unix 都共享此内容。

Jde*_*eBP 15

80 列控制台上只有 9 个制表位。

让我们首先扩展评论。您实际上还没有测试任何控制台。这些都是 GUI终端模拟器程序。幸运的是,实际的控制台(至少在 Linux 和 FreeBSD 上)也表现出这种行为。

但这不是你声称的行为。它没有展示 11 个制表位。它演示了 9。

要正确理解制表位,您需要抛弃“它们总是扩展到 N 个空格”。主意。如果您可以使用带有制表位的机械打字机,那么去看看它是如何工作的(制表符是通过托架上突出的销钉设置的)。Unix 和 Linux 世界中终端中的制表符就是这样工作的

(可以配置行规则来制定制表符,在它们到达终端之前用空格字符替换输出流中的制表符。我们正在讨论制表符,由终端本身而不是由行规则执行,尽管如此. 使用硬制表符,TAB会向终端发送一个真正的字符。相反,使用软制表符时,您描述的行为首先不会发生。尝试设置stty tab3并查看。)

初始化时,终端根本没有设置制表位。早期getty,或reset命令或类似的东西(它因系统而异),输出一系列空格字符和转义序列,在特定列设置制表位。有时,列甚至转义序列都是硬连线的。但是,大多数情况下,该命令会在 termcap/terminfo 数据库中查找信息。提供清除所有选项卡的转义序列的 terminfo 功能是tbc;该hts功能提供了在当前列设置选项卡的转义序列;并且该 it功能默认指定制表位之间的列数。查找termcap,等效能力itctst.

该命令获取终端大小(从内核,通过系统调用,或从 terminfo/termcap 记录的另一部分),查看列数,并重复输出it空格后跟st尽可能多的列的序列,最后输出一个回车返回到#0列。这正是在许多机械打字机上设置一组制表位的方式:重复空格到下一个制表位,点击“设置制表位”杆将销推出,重复直到右边距,将托架推回左侧.

对于 80 列宽的终端,it通常给出 8,该命令执行九次,在第 8、16、24、32、40、48、56、64 和 72 列设置制表位。

第 0 列没有制表位(除非程序错误)。这是您的第一个计数错误。第 80 列中也没有制表符,这是您的第二个计数错误。“为什么”,你可能会问,“是吗?” 首先,这是因为它不可能80列设置一个制表位。光标在 80 列终端上从第 0 列移动到第 79 列。

其次,必须考虑TAB角色的行为。它并没有,流行的看法相反,扩展到足够的空间移动到8列的下一个倍数。记住,Unix 和 Linux 终端就像机械打字机一样工作。在机械打字机的世界中,Tab 键移动笔架,直到它被制表销或右边距停止。(一些聪明的木屐此时可能会提到保证金释放。这是机械打字机具有而 Unix 和 Linux 终端没有的东西,原因显而易见。)

TABUnix 和 Linux 终端上的字符实际上以相同的方式工作。作为响应,终端会发出空格,直到到达下一个设置的制表位或最右边的列。这就是这里发生的事情。当光标在第 79 列时,你可以写任意多的TAB字符,什么都不会发生。除了最右边的列之外没有制表符。

termcap/terminfo 系统允许可设置的硬标签是可选的。但是您测试过的所有程序都提供了“xterm”类型的终端仿真器。(它理解的转义序列是xterm在 termcap/terminfo 数据库中的条目/条目中给出的那些。)现在,FreeBSD 虚拟控制台中的终端模拟器也是如此。Linux 虚拟控制台中的终端模拟器实际上是一种稍微不同的终端类型,指定为linux. (FreeBSD 虚拟控制台曾经也是这种情况,它有一个cons25类型。FreeBSD 在 9.0 版中将其内核终端仿真器更改为与 xterm 兼容,作为在虚拟控制台上获得 UTF-8 支持的项目的一部分。)所有这些类似 xterm 的程序以及 Linux 内核终端仿真器都具有完整的可设置硬标签机制。

(对于那些谁喜欢阅读源:看TabNext()TabToNextStop()功能tabs.cxterm。源代码)

你可以自己试试。运行这些命令:

tbc=$(tput tbc) hts=$(tput hts)
printf '%s\r%s\r\n' "${tbc}" "a${hts}aaa${hts}aaaaaaa${hts}aaaaaaaaaaaaa${hts}aaaaaaaaaaaaaaaaaa${hts}"
Run Code Online (Sandbox Code Playgroud)

然后再次运行您的命令。正好设置了五个制表位。计算${hts}命令中的 ,或查看a字符行。在第五个之后,这个TAB角色使那辆比喻性的打字机马车在剩下的所有道路上都飞快地移动到右侧边缘。

下一个(打印,非TAB)字符会导致自动回车到左边距,并换行到下一行。在 termcap/terminfo 术语中,xterm终端类型具有自动边距。理论上,Unix 和 Linux 可以与没有自动边距的终端一起工作。printf无论你输出多少,你的命令都会卡在第 79 列,直到你写了一个回车。在实践中,这样的设备会让当今大多数人感到困惑。