cat*_*cat 19 command-line terminal ascii newlines
Unix / Linux EOL 是 LF、换行、ASCII 10、转义序列\n。
这是一个 Python 代码片段,可以准确地获得一个按键:
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
Run Code Online (Sandbox Code Playgroud)
当我按下Enter键盘以响应此代码段时,它给出了\r回车符 ASCII 13。
在Windows 上,Enter发送CR LF == 13 10. *nix 不是 Windows;为什么Enter给 13 而不是 10?
Tho*_*key 30
本质上是“因为自从手动打字机以来它就已经这样做了”。真的。
手动打字机有一个托架,可在其上进纸,并在您打字时向前移动(加载弹簧),并且有一个杠杆或钥匙可以释放托架,让弹簧将托架返回到左边距。
随着电子数据输入(电传打字机等)的引入,他们继续推进。因此Enter,许多终端上的密钥将被标记为Return。
在将托架返回到左边距后(在手动过程中)发生换行。再次,电子设备模仿手动设备,进行单独line-feed操作。
这两个操作都被编码(允许电传打字机不仅仅是创建纸张类型的独立设备),所以我们有CR(回车)和LF(换行)。这张来自ASR 33 Teletype Information 的图片显示了键盘,Return在右侧,Line-Feed在左侧。在右边,这是主要的关键:
Unix 后来出现。它的开发人员喜欢缩短东西(看看所有的缩写,即使creat是“create”)。面对可能由两部分组成的过程,他们决定换行只有在它们前面有回车时才有意义。因此,他们从files 中删除了显式回车,并转换了终端的Return密钥以发送相应的换行符。为了避免混淆,他们将换行称为“换行”。
在终端上写入文本时,Unix 会转换为另一个方向:换行符变为回车符/换行符。
(即,“通常”:所谓的“熟模式”,与不进行翻译的“原始”模式形成对比)。
概括:
Nom*_*mal 11
虽然Thomas Dickey 的回答非常正确,但 Stéphane Chazelas 在对 Dickey 的回答的评论中正确地提到转换并非一成不变;它是生产线纪律的一部分。
事实上,翻译是完全可编程的。
在男子3周的termios手册页基本上包含所有相关信息。(该链接指向Linux 手册页项目,其中确实提到了哪些功能仅适用于Linux,哪些功能是 POSIX 或其他系统所共有的;请始终检查那里每个页面上的符合部分。)
所述iflag终端属性(old_settings[0]在问题中所示的代码的Python)具有对所有POSIXy系统三个相关国旗:
INLCR: 如果设置,则在输入时将 NL 转换为 CRICRNL: 如果设置(并且IGNCR未设置),则在输入时将 CR 转换为 NLIGNCR: 忽略输入的 CR同样,也有相关的输出设置(old_settings[1]):
OPOST: 启用输出处理。OCRNL:在输出时将 CR 映射到 NL。ONLCR: 在输出时将 NL 映射到 CR。(XSI;并非在所有 POSIX 或 Single-Unix-Specification 系统中都可用。)ONOCR: 跳过(不输出)第一列的 CR。ONLRET: 跳过(不输出)CR。例如,您可以避免依赖tty模块。“makeraw”操作只是清除一组标志(并设置CS8oflag):
import sys
import termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
ch = None
try:
new_settings = termios.tcgetattr(fd)
new_settings[0] = new_settings[0] & ~termios.IGNBRK
new_settings[0] = new_settings[0] & ~termios.BRKINT
new_settings[0] = new_settings[0] & ~termios.PARMRK
new_settings[0] = new_settings[0] & ~termios.ISTRIP
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.IGNCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IXON
new_settings[1] = new_settings[1] & ~termios.OPOST
new_settings[2] = new_settings[2] & ~termios.CSIZE
new_settings[2] = new_settings[2] | termios.CS8
new_settings[2] = new_settings[2] & ~termios.PARENB
new_settings[3] = new_settings[3] & ~termios.ECHO
new_settings[3] = new_settings[3] & ~termios.ECHONL
new_settings[3] = new_settings[3] & ~termios.ICANON
new_settings[3] = new_settings[3] & ~termios.ISIG
new_settings[3] = new_settings[3] & ~termios.IEXTEN
termios.tcsetattr(fd, termios.TCSANOW, new_settings)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
Run Code Online (Sandbox Code Playgroud)
尽管出于兼容性考虑,您可能希望首先检查 termios 模块中是否存在所有这些常量(如果您在非 POSIX 系统上运行)。您还可以使用new_settings[6][termios.VMIN]和new_settings[6][termios.VTIME]来设置在没有挂起数据的情况下读取是否会阻塞,以及多长时间(以整数分秒为单位)。(通常VMIN设置为 0,VTIME如果读取应立即返回,则设置为 0,或者设置为正数(十分之一秒)读取最多应等待多长时间。)
如您所见,上述(以及一般的“makeraw”)禁用了输入的所有翻译,这解释了 cat 所看到的行为:
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IGNCR
Run Code Online (Sandbox Code Playgroud)
要获得正常行为,只需省略清除这三行的行,即使在“原始”时输入翻译也不会改变。
该new_settings[1] = new_settings[1] & ~termios.OPOST行禁用所有输出处理,不管其他输出标志怎么说。您可以省略它以保持输出处理完整。即使在原始模式下,这也使输出保持“正常”。(它不影响输入是否自动回显;这是由 中的ECHOcflag控制的new_settings[3]。)
最后,当设置新属性时,如果设置了任何新设置,则调用将成功。如果设置是敏感的——例如,如果你在命令行上要求输入密码——你应该获取新设置,并验证重要标志是否正确设置/取消设置,以确保。
如果要查看当前的终端设置,请运行
stty -a
Run Code Online (Sandbox Code Playgroud)
输入标志通常在第四行,输出标志在第五行,-如果未设置标志,则在标志名称前面加上一个。例如,输出可能是
speed 38400 baud; rows 58; columns 205; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
Run Code Online (Sandbox Code Playgroud)
在伪终端和 USB TTY 设备上,波特率无关紧要。
如果您编写希望读取密码等 Bash 脚本,请考虑以下习惯用法:
#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0
Run Code Online (Sandbox Code Playgroud)
在EXIT每当外壳退出执行陷阱。在stty -g读取在脚本的开始时的终端的当前设置,所以当前设置当脚本退出时,自动恢复。你甚至可以用Ctrl+中断脚本C,它会做正确的事情。(在某些带有信号的极端情况下,我发现终端有时会卡在原始/非规范设置中(需要在终端上键入reset+Enter盲目),但stty sane在恢复实际原始设置之前运行已经解决了每次我。所以这就是它在那里的原因;一种额外的安全。)
您可以使用read内置的 bash读取输入行(未回显到终端),甚至可以使用
IFS=$'\0'
input=""
while read -N 1 c ; do
[[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
input="$input$c"
done
Run Code Online (Sandbox Code Playgroud)
如果您没有设置IFS为 ASCII NUL,read内置将使用分隔符,因此c它将为空。年轻球员的陷阱。