您遇到的问题是“raw”、“cooked”和“cbreak”模式之间的区别。这些模式是内核级终端驱动程序的模式,而不是您的应用程序代码或标准库或用户空间中的任何其他模式。这是引用这些的老式 Unix 方式。Posix 用一组更细粒度的属性取代了它们,尽管 Posix 属性通常与辅助函数一起以模仿旧的“raw”、“cooked”和“cbreak”模式的方式翻转。
在熟模式下,终端驱动程序本身具有内置的原始行编辑功能。它处理退格、单词擦除(基本上一次退格整个单词)和类似的事情。没有什么比处理箭头键或历史记录或类似的东西更复杂了。很原始。在这种模式下,您的程序在发送行尾 (eol) 字符之前永远不会从终端看到任何内容,然后您的程序会得到一整行,并且\n
无论终端实际是什么,都会将行结尾转换为 Unix 标准做。此外,作为其中的一部分,终端驱动程序将输入的字符回显到终端,以便用户可以看到他们正在输入的内容。
在 'cooked' 模式下,内核级终端驱动程序也会进行一些输出转换。如果需要,其中一部分会\n
变成。\r\n
此外,在“熟”模式下,终端驱动程序处理特殊字符,如 Control-C(向控制进程组发送 SIGINT(由 CPython 转换为 KeyboardInterrupt 异常))和 Control-Z(发送 SIGTSTP(如 SIGSTOP,但可以被捕获)到控制进程组)。
在“cbreak”模式下,不再进行行编辑。终端驱动程序立即将每个字符(或短字符序列,如箭头键的转义序列)提供给程序。这些字符不会回显到屏幕上,因此除非您的程序随后打印它们,否则用户将看不到它们。尽管终端驱动程序仍然处理特殊字符,如 Control-C 和 Control-Z,但它不再处理行编辑字符,如退格或字擦除字符(通常是 Control-W)。此外,一些输出处理仍然完成,因此驱动程序将 a\n
变为 a \r\n
。
在“原始”模式下,不对输入或输出进行任何处理。没有特殊的字符处理,没有回显,没有转换\n
成\r\n
,没有对 Control-Z 的处理,什么都没有。这取决于将终端置于原始模式的程序来完成这一切。
现在,您正在设置 的属性,sys.stdin
因此您可能认为这不应该影响sys.stdout
. 但是,事实上,您的两个文件描述符都会导致终端驱动程序的完全相同的“实例”。决定发生什么的是终端驱动程序的设置。sys.stdin
因此,如果您通过、sys.stdout
或什至更改这些设置都没有关系sys.stderr
,它们都会更改相同的底层终端驱动程序实例,并且它们会影响所有其他设置。
当然,对于在程序启动之前由 shell 重定向的文件描述符来说,情况并非如此。
附带说明一下,您可以stty -a
在命令行上使用 来查看所有这些标志的完整读数(包括哪些控制字符导致了cbreak 和cbreak 模式下的哪些信号)。