Kus*_*nda 6 bash ksh tty user-input read
我一直试图弄清楚如果我Ctrl+D使用read -N 1inbash和ksh93.
我知道传输结束字符和文件结束条件之间的区别,并且我知道Ctrl+D在使用readwithout时会做什么-N(它发送 EOT,如果输入为空,则底层read()返回零,发出信号EOF)。
但我不确定为什么尝试读取特定数量的字符会如此彻底地改变这种行为。我会期望 EOF 条件并且以下循环将退出:
while read -N 1 ch; do
printf '%s' "$ch" | od
done
Run Code Online (Sandbox Code Playgroud)
按下时输出Ctrl+D:
0000000 000004 0000001
该bash手册说,关于read -N(ksh93也有类似的措辞):
-N nchars; read 在完全读取nchars字符后返回,而不是等待完整的输入行,除非遇到 EOF或读取超时。
...但它没有说明将 TTY 切换到原始/无缓冲模式(这是我假设正在发生的情况)。
该-n选项read似乎在关于以同样的方式工作Ctrl+D,并且要读取的字符数目似乎没有任何关系。
我如何read -N向循环发出结束输入信号并退出循环(除了测试读取的值),为什么这与“裸”不同read?
如果文档指出没有 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);
}
Run Code Online (Sandbox Code Playgroud)
在终端上尝试一下,您会看到规范模式下的终端驱动程序只是解释 EOT 以完成任何未完成的读取,并且它会在内部进行缓冲,直到它看到一些规范的输入终止符,而不管读取缓冲区的大小(键入长于 32 的行字节)。
令你困惑的文字¸
除非遇到EOF
指的是真正的EOF。