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 秒睡眠后打印,因此我推断从内核到终端的输出是行缓冲的。所以现在,我的问题是:
\b\b
回退了两个空格,到了 (second) 的位置l
,那么和怎么l
被替换的差不多h
,o
应该已经被替换了\n
。为什么不是?printf("h\n");
,它会打印Hello
并返回两个字符,而不会擦除。我从其他答案中得到的这是因为非破坏性退格。为什么输入和输出的这种行为不同?也就是说,如果我在终端中输入一些东西(即使是同一个程序)并按 Backspace,它会擦除最后一个字符,但不会擦除输出。为什么?如果有帮助,我在 xterm 终端上的 Ubuntu 系统上使用 bash。
首先,整个事情只在 5 秒睡眠后打印,因此我推断从内核到终端的输出是行缓冲的。
不,从您的程序到内核的输出是行缓冲的。这是stdio
whenstdout
是终端的默认行为。添加setbuf(stdout, NULL)
关闭输出缓冲的调用stdout
。见setbuf(3)
。
- 既然
\b\b
回退了两个空格,到了那个位置l
就跟l
被替换的差不多了h
,o
应该已经被替换了\n
。为什么不是?
因为换行符只是移动光标(并滚动屏幕),所以它不会打印为一个可见字符,会占据终端上的字形位置。如果我们假设它会代替字形,它会是什么样子?
- 如果我在同一个程序中输入一些东西,然后按退格键,它会删除最后一个字符,但不会删除输出。为什么?
好吧,当您键入时会发生什么取决于终端所处的模式。粗略地说,它可以处于通常的“熟”模式,终端本身提供基本的行编辑(处理退格);或者在“原始”模式下,所有按键都进入应用程序,由应用程序决定如何处理它们,以及输出什么作为响应。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 read
s 和write
s x
,将其输出到终端。然后读取退格字符(八进制0177或十进制127),输出退格字符(八进制010,十进制8 (*)),将光标移回并输出清除行尾的控制序列,<ESC>[K
. 最后一个\4
是Ctrl-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 连接的远程端。对于用户空间软件来说这无关紧要。
代码(\b
等\n
)移动光标,而不是文本。
我想你会发现这种行为源于“美好的过去”,当时我们必须操纵打印机上的光标才能在电传打字机、高尔夫球打印机等上获得粗体字体和删除线等令人惊叹的效果。
\b
只需将插入点向后移动并允许您改写。在数字屏幕上,这会删除以前的内容,但在旧打印机上,您会得到覆盖的字符。
\n
将光标移动到新行,但将旧行上的文本保留在后面。这将是一个 CRLF,它将光标放回行首并告诉打印机向上滚动一行。
CR 、LF 和 CRLF 代码再次与操作旧设备以获得这些新奇效果的需要相关。
..... 并且没有任何方法可以从打印页面中删除字符,您应该在发布打印之前将其正确。
编辑
回复第 2 点。输入和输出执行不同的操作,因为您告诉它们这样做。
\b
相当于向左箭头关闭 INSERT,而不是删除。
\n
相当于向下箭头 (LF) 和 HOME (CR),而不是“输入”键。
首先,如果你想了解这些东西,你应该阅读
\n\n你问,
\n\n\n\n\n\xe2\x80\xa6 如果我输入 \xe2\x80\xa6 内容,然后按 Backspace,\n 它会删除最后一个字符,但不会删除输出。\xe2\x80\x83为什么?
\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 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\n当你按下退格键时,\n 内核 tty 行规则会通过打印(在简单的情况下)Ctrl-H、一个空格,然后再打印另一个 Ctrl-H 来擦掉你之前的字符。
\n
\n 来源:当您在输入文本时键入退格键时,Unix 如何擦除内容\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 是另一个故事。