我一直试图弄清楚为什么如果我Ctrl+D用read -N 1
inbash
和读取,我的变量中会得到一个文字传输结束字符(EOT,ASCII 代码 4) ksh93
。
我知道传输结束字符和文件结束条件之间的区别,并且我知道Ctrl+D使用read
without时会发生什么-N
(它发送 EOT,如果输入为空,则底层read()
返回零,发出信号) EOF)。
但我不确定为什么尝试读取特定数量的字符会如此彻底地改变这种行为。我预计会出现 EOF 条件并且以下循环将退出:
while read -N 1 ch; do
printf '%s' "$ch" | od
done
按下时输出Ctrl+D:
0000000 000004 0000001
手册bash
上说read -N
(ksh93
有类似的措辞):
-N nchars
; read 在准确读取字符后返回nchars
,而不是等待完整的输入行,除非遇到EOF或读取超时。
...但它没有提到将 TTY 切换到原始/无缓冲模式(我认为这是正在发生的情况)。
选项-n
似乎read
与 的工作方式相同Ctrl+D,并且要读取的字符数似乎也不重要。
我如何发出输入结束信号read -N
并退出循环(除了测试读取的值),为什么这与“裸”不同read
?
答案1
如果文档指出不存在 ASCII EOF 这样的东西,^D 的 ASCII 语义是 EOT,这是终端驱动程序在规范模式下提供的内容,这可能会更有帮助:它结束当前传输,read
.程式解释0 长度读取为 EOF,因为这就是 EOF 在具有该文件的文件上的样子,但终端驱动程序拒绝提供字符代码 4,而是吞掉它并终止读取并不总是您想要的。
这就是这里发生的事情:控制字符语义是规范模式的一部分,在该模式中,终端驱动程序会进行缓冲,直到看到约定为其分配了特殊含义的字符。 EOT、BS、CR 和许多其他内容都是如此(请参阅stty -a
并man termios
了解所有血淋淋的细节)。
read -N
是仅传递接下来的 N 个字符的显式命令。为此,shell 必须停止向终端驱动程序询问规范语义。
顺便说一句,EOF 实际上并不是终端可以设置或输入的条件。
如果您继续阅读其他任何内容上的 eof,您将不断获得 EOF 指示符,但终端驱动程序可以提供的唯一 EOF 是一个假 EOF — 想想看 — 如果终端驱动程序实际上提供了一个真正的 EOF,那么 shell之后也无法继续阅读。都是同一个终端。这里:
#include <unistd.h>
#include <stdio.h>
char s[32];
int main(int c, char**v)
{
do {
c=read(0,s,sizeof s);
printf("%d,%.*s\n",c,c,s);
} while (c>=0);
}
在终端上尝试一下,您会看到规范模式下的终端驱动程序只是解释 EOT 来完成任何未完成的读取,并且它会在内部进行缓冲,直到看到一些规范输入终止符,无论读取缓冲区大小如何(键入长度超过 32 的行)字节)。
令您困惑的文字¸
除非遇到EOF
指的是真正的 EOF。