这是关于退格 ( ) 字符的行为\b
。我有以下 C 程序:
int main() {
printf("Hello\b\b");
sleep(5);
printf("h\n");
return 0;
}
我的终端上的输出是
Helho
光标前进到下一行的第一个位置。
首先,整个内容仅在 5 秒睡眠后打印,因此我推断从内核到终端的输出是行缓冲的。所以现在,我的问题是:
- 由于
\b\b
向后退了两个空格,到了 (第二个) 的位置,那么与被 替换的l
方式类似,应该被 替换。为什么不是呢?l
h
o
\n
- 如果我删除该行
printf("h\n");
,它会打印Hello
并返回两个字符,而不删除。我从其他答案中得到的这是因为非破坏性退格键。为什么输入和输出的行为不同?也就是说,如果我在终端中输入某些内容(即使是相同的程序)并按 Backspace,它会删除最后一个字符,但不会删除输出。为什么?
我在 Ubuntu 系统上的 xterm 终端上使用 bash,如果有帮助的话。
答案1
首先,整个内容仅在 5 秒睡眠后打印,因此我推断从内核到终端的输出是行缓冲的。
不,你的输出程序到内核是行缓冲的。这是stdio
当stdout
是终端时的默认行为。添加调用setbuf(stdout, NULL)
以关闭 的输出缓冲stdout
。看setbuf(3)
。
- 由于
\b\b
向后退了两个空格,到了 then 的位置,与被 替换的l
方式类似,所以应该被 替换。为什么不是呢?l
h
o
\n
因为换行符只是移动光标(并滚动屏幕),所以它不会打印为可见字符来占据终端上的字形位置。如果我们假设它会取代字形,它会是什么样子?
- 如果我在同一个程序中输入某些内容,然后按 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
和,write
将x
其输出到终端。然后读取退格符(八进制字符代码 0177 或十进制字符代码 127),并输出退格字符(八进制 010,十进制 8 (*)),将光标向后移动和输出控制序列用于清除行尾,<ESC>[K
.最后一个\4
是Ctrl-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
,这很令人困惑。
许多用户养成了打字的习惯 Backspace Space Backspace ,以便使用空格来替换/销毁坏字符;即,手动擦除他们。最终(1979 年或 20 世纪 80 年代初),开发人员态度软化并使其自动执行:
当您按下退格键时,内核 tty 行规则会通过打印(在简单情况下)Ctrl-H、一个空格,然后再打印另一个 Ctrl-H 来擦掉您之前的字符。
来源:当你在输入文本时按退格键,Unix 如何删除内容
其中“Ctrl-H”是退格键的控制代码。
在某些情况下,您可以关闭此选项并通过键入stty -echoe
( echoe
=“echo擦除”,并且-
意思是“不要这样做”)。
____
1事实上,在很早的时候,退格键并不是 Backspace——但那是另一个故事了。