终端上退格键的行为

终端上退格键的行为

这是关于退格 ( ) 字符的行为\b。我有以下 C 程序:

int main() {
    printf("Hello\b\b");
    sleep(5);
    printf("h\n");
    return 0;
}

我的终端上的输出是

Helho

光标前进到下一行的第一个位置。

首先,整个内容仅在 5 秒睡眠后打印,因此我推断从内核到终端的输出是行缓冲的。所以现在,我的问题是:

  1. 由于\b\b向后退了两个空格,到了 (第二个) 的位置,那么与被 替换的l方式类似,应该被 替换。为什么不是呢?lho\n
  2. 如果我删除该行printf("h\n");,它会打印Hello并返回两个字符,而不删除。我从其他答案中得到的这是因为非破坏性退格键。为什么输入和输出的行为不同?也就是说,如果我在终端中输入某些内容(即使是相同的程序)并按 Backspace,它会删除最后一个字符,但不会删除输出。为什么?

我在 Ubuntu 系统上的 xterm 终端上使用 bash,如果有帮助的话。

答案1

首先,整个内容仅在 5 秒睡眠后打印,因此我推断从内核到终端的输出是行缓冲的。

不,你的输出程序到内核​​是行缓冲的。这是stdiostdout是终端时的默认行为。添加调用setbuf(stdout, NULL)以关闭 的输出缓冲stdout。看setbuf(3)

  1. 由于\b\b向后退了两个空格,到了 then 的位置,与被 替换的l方式类似,所以应该被 替换。为什么不是呢?lho\n

因为换行符只是移动光标(并滚动屏幕),所以它不会打印为可见字符来占据终端上的字形位置。如果我们假设它会取代字形,它会是什么样子?

  1. 如果我在同一个程序中输入某些内容,然后按 Backspace,它会删除最后一个字符,但不会删除输出。为什么?

好吧,当您键入时会发生什么取决于终端所处的模式。粗略地说,它可以处于通常的“熟”模式,其中终端本身提供基本的行编辑(处理退格键);或者在“原始”模式下,所有按键都进入应用程序,由应用程序决定如何处理它们以及输出什么作为响应。 Cooked 模式通常与“本地回显”一起使用,其中终端(用户本地)在键入字符时打印出字符。在原始模式下,应用程序通常负责回显键入的字符,以完全控制可见内容。

有关终端模式的讨论,请参阅此问题: “原始”和“熟的”设备驱动程序有什么区别?

如果运行 eg cat,终端将处于烘焙模式(默认)并处理行编辑。例如,点击xBackspaceCtrl-D将导致cat仅读取空输入,表示输入结束。您可以使用 检查这一点strace。但是,如果您运行交互式 Bash shell,它将自行处理退格键,并输出它认为适合执行用户期望的操作,即擦除一个字符。


strace -etrace=read,write -obash.trace bash输入上述序列后,以下是 的输出的一部分xBackspaceCtrl-D

read(0, "x", 1)                         = 1
write(2, "x", 1)                        = 1
read(0, "\177", 1)                      = 1
write(2, "\10\33[K", 4)                 = 4
read(0, "\4", 1)                        = 1

首先,Bashread和,writex其输出到终端。然后读取退格符(八进制字符代码 0177 或十进制字符代码 127),并输出退格字符(八进制 010,十进制 8 (*)),将光标向后移动输出控制序列用于清除行尾,<ESC>[K.最后一个\4Ctrl-D,Bash 使用它来退出程序。

(* 在输入中,Ctrl-H将具有十进制字符代码 8。退格键与此处相同或为 127,这又取决于终端的设置方式。)

相比之下,相同的实验cat仅显示单次读取零字节,即“文件结尾”条件。文件结尾可能意味着连接的管道或套接字被关闭、实际的文件结尾或Ctrl-D以烘焙模式从终端接收:

read(0, "", 131072)                     = 0

特别是,cat看不到x、退格键或 的实际代码Ctrl-D:它们是由终端处理的。这可能是内核中的虚拟终端驱动程序;通过串行连接等的实际物理终端;或者像 xterm 这样的终端模拟器,可以在同一台机器上运行,也可以在 SSH 连接的远程端运行。对于用户空间软件来说并不重要。

答案2

代码(\b\n)移动光标,而不是文本。

我想你会发现这种行为源于“美好的过去”,当时我们必须操纵打印机上的光标才能在电传打字机、高尔夫球打印机等上获得粗体字体和删除线等令人惊叹的效果。

\b只需将插入点向后移动并允许您改写。在数字屏幕上,这会删除以前的内容,但在旧打印机上,您会得到覆盖的字符。

\n将光标移动到新行,但将旧行上的文本保留在后面。这将是一个 CRLF,它将光标放回行首并告诉打印机向上滚动一行。

CR、LF 和 CRLF 代码再次与操纵旧设备以获得这些新奇效果的需要相关。

..... 并且没有任何方法可以从打印页面中删除字符,您应该在发布打印之前将其正确。

编辑

回复第 2 点。输入和输出执行不同的操作,因为您告诉它们这样做。

\b相当于向左箭头关闭 INSERT,而不是删除。

\n相当于向下箭头 (LF) 和 HOME (CR),而不是“输入”键。

答案3

首先,如果你想了解这些东西,你应该阅读

你问,

...如果我输入一些内容...,然后按 Backspace,它会删除最后一个字符,但不会删除输出。为什么?

这里有一些琐事要告诉你:在 Unix 的早期(20 世纪 70 年代,Linux 还没有出现之前),退格键1 没有删除输入的字符,用户讨厌它。你输入beast,按退格键三下,你仍然会用邪恶的眼睛be|ast盯着你(|代表光标)。即使在你输入之后st(因为你一直想说best),屏幕仍然会显示best|t,这很令人困惑。

许多用户养成了打字的习惯 BackspaceSpaceBackspace ,以便使用空格来替换/销毁坏字符;即,手动擦除他们。最终(1979 年或 20 世纪 80 年代初),开发人员态度软化并使其自动执行:

当您按下退格键时,内核 tty 行规则会通过打印(在简单情况下)Ctrl-H、一个空格,然后再打印另一个 Ctrl-H 来擦掉您之前的字符。
来源:当你在输入文本时按退格键,Unix 如何删除内容

其中“Ctrl-H”是退格键的控制代码。

在某些情况下,您可以关闭此选项并通过键入stty -echoe ( echoe =“echo擦除”,并且-意思是“不要这样做”)。
____
1事实上,在很早的时候,退格键并不是 Backspace——但那是另一个故事了。

相关内容