终端退格的行为

for*_*tor 5 c terminal tty stdio

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

int main() {
    printf("Hello\b\b");
    sleep(5);
    printf("h\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我终端上的输出是

Helho
Run Code Online (Sandbox Code Playgroud)

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

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

  1. 既然\b\b回退了两个空格,到了 (second) 的位置l,那么和怎么l被替换的差不多ho应该已经被替换了\n。为什么不是?
  2. 如果我删除 line printf("h\n");,它会打印Hello并返回两个字符,而不会擦除。我从其他答案中得到的这是因为非破坏性退格。为什么输入和输出的这种行为不同?也就是说,如果我在终端中输入一些东西(即使是同一个程序)并按 Backspace,它会擦除​​最后一个字符,但不会擦除输出。为什么?

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

ilk*_*chu 6

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

不,从您的程序到内核的输出是行缓冲的。这是stdiowhenstdout是终端的默认行为。添加setbuf(stdout, NULL)关闭输出缓冲的调用stdout。见setbuf(3)

  1. 既然\b\b回退了两个空格,到了那个位置l就跟l被替换的差不多了ho应该已经被替换了\n。为什么不是?

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

  1. 如果我在同一个程序中输入一些东西,然后按退格键,它会删除最后一个字符,但不会删除输出。为什么?

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

有关终端模式的讨论,请参见例如这个问题: “原始”和“熟”设备驱动程序之间的区别是什么?

如果您运行 eg cat,终端将处于熟模式(默认)并处理行编辑。例如,点击xBackspaceCtrl-D将导致cat仅读取空输入,发出 input 结束的信号。您可以使用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
Run Code Online (Sandbox Code Playgroud)

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

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

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

read(0, "", 131072)                     = 0
Run Code Online (Sandbox Code Playgroud)

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


bu5*_*man 5

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

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

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

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

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

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

编辑

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

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

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


G-M*_*ca' 5

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

\n\n\n\n

你问,

\n\n
\n

\xe2\x80\xa6 如果我输入 \xe2\x80\xa6 内容,然后按 Backspace,\n 它会删除最后一个字符,但不会删除输出。\xe2\x80\x83为什么?

\n
\n\n

这里 \xe2\x80\x99 为您提供一些琐事:在 Unix 的早期\n(1970 年代,在 Linux 出现之前),\n退格键 1 并没有 \xe2\x80\x99删除输入字符,用户讨厌\xc2\xa0\n你\xe2\x80\x99d 输入beast,按退格键三下,\n你\xe2\x80\x99d 仍然邪恶地盯着be|ast你\n(|代表光标)。\xc2\ xa0\n即使在你输入之后st(因为你一直想说best),\n屏幕仍然会显示best|t,这很令人困惑。

\n\n

许多用户习惯于为每个要删除的字符键入\n Backspace\xe2\x80\xaf Space\xe2\x80\xaf Backspace\nonce,\n以便使用空格来替换\xe2\x80\x8a/\xe2\x80 \x8 销毁坏字符;\ni.e.,手动删除它们。\xc2\xa0\n最终(1979 年或 20 世纪 80 年代初),\n开发人员态度软化并使其自动执行:

\n\n
\n

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

\n
\n\n

其中 \xe2\x80\x9cCtrl-H\xe2\x80\x9d 是退格键的控制代码。

\n\n

在某些情况下,您可以关闭此选项\n并通过键入stty\xe2\x80\xaf-echoe\n( echoe\xc2\xa0= \xe2\x80\x9cecho擦除\xe2\x80\x9d,并-表示 \xe2\x80\x9cdon\xe2\ x80\x99t 执行\xe2\x80\x9d)。
\n____
\n 1实际上,在很早的时候,\n退格键不是\xe2\x80\x99t 退格键\xe2\x80\x94,但\xe2\x80\x99 是另一个故事。

\n