您可能会认为,由于每个选项卡默认向前移动 8 列,因此 80 列的控制台将恰好有 10 个制表位。但事实并非如此:
$ printf "1\t2\t3\t4\t5\t6\t7\t8\t9\t0\ta"
输出结果应该为 2 行,因为有 10 个制表位,占据了所有 80 列的空间,再加上一个字符,该字符将被推到下一行。但该命令在 xterm、gnome-terminal 和 rxvt 中只生成一行输出。
所发生的是,最后一个制表位神奇地少占用了一个字符的空间。
同时,expand(1)认为制表符的间距为8,因此
$ printf "1\t2\t3\t4\t5\t6\t7\t8\t9\t0\ta" | expand
会神奇地在 80 列控制台上产生 2 行。
这似乎是某种标准。但我无法通过谷歌搜索找到来源。
仅在 Linux 上测试过,但恐怕所有 unix 都存在同样的问题。
答案1
80 列控制台上只有 9 个制表位。
我们先来详细看看这条评论。你还没有真正测试过任何控制台。这些都是 GUI终端仿真器程序。幸运的是,实际的控制台(至少在 Linux 和 FreeBSD 上)也表现出这种行为。
但这不是您所声称的行为。它没有显示 11 个制表位。它只显示了 9 个。
要正确理解制表位,您需要抛弃“它们总是扩展到 N 个空格”的想法。如果您有带制表位的机械打字机,那就去看看它是如何工作的(制表位由托架上突出的销钉设置)。Unix 和 Linux 世界中终端中的制表位以这种方式工作。
(可以配置线路纪律来实施柔软的tabs,在输出流中将制表符替换为空格字符,然后才能到达终端。我们正在讨论难的制表符,由终端本身执行,而不是由线路规则执行,但在这里。使用硬制表符时,会将真实的TAB
字符发送到终端。相反,使用软制表符时,您描述的行为不会首先发生。尝试设置stty tab3
并查看。)
初始化后,终端具有根本没有设置制表位。早期,getty
,或reset
命令或类似命令(根据系统而变化),输出一系列空格字符和转义序列,设置制表位在特定列。有时列甚至转义序列都是硬连线的。但最常见的情况是,该命令在 termcap/terminfo 数据库中查找信息。terminfo 功能给出清除所有制表符的转义序列是tbc
;hts
功能给出在当前列设置制表符的转义序列;功能 it
默认指定制表位之间的列数。对于 termcap,等效功能是it
、ct
和st
。
该命令获取终端大小(从内核、通过系统调用或从 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 终端的工作方式类似于机械打字机。在机械打字机的世界里,制表键会移动托架,直到它被制表位销或右边距停止。(一些聪明人可能会提到释放保证金此时。这是机械打字机具有而 Unix 和 Linux 终端所没有的功能,原因显而易见。)
TAB
Unix 和 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.c
xterm
你可以自己尝试一下。运行以下命令:
tbc=$(tput tbc) hts=$(tput hts)
printf '%s\r%s\r\n' "${tbc}" "a${hts}aaa${hts}aaaaaaa${hts}aaaaaaaaaaaaa${hts}aaaaaaaaaaaaaaaaaa${hts}"
然后再次运行你的命令。完全正确五制表位已设置。计算${hts}
命令中的字符,或查看字符行a
。除第五个字符外,该TAB
字符使隐喻打字机托架将其余所有字符快速移动到右侧边缘。
下一个(打印、非TAB
)字符导致自动的回车到左边距,换行到下一行。在 termcap/terminfo 术语中,xterm
终端类型有自动边距理论上,Unix 和 Linux 可以使用没有自动边距的终端。printf
无论您输出多少,您的命令都会卡在第 79 列,直到您输入回车符。实际上,这种设备会让如今的大多数人感到困惑。