Bash 的实现read
允许您使用标志指定要读取的最大字符数-n
,或者使用标志指定替代终止字符-d
。这些选项都不适用于标准终端输入,因为通常终端驱动程序将输入保存在自己的内部缓冲区中,直到用户键入 ENTER 键(或某些其他击键,如 Control-C 或 Control-D)。
例如,背后的想法read -n1 char
是您希望read
用户输入单个字符后立即返回,而不是您希望read
等待用户输入完整的行然后返回该行的第一个字符。read -d';' command
同样,只要用户输入分号,该命令就会返回;同样,等待用户输入完整的行,然后只返回分号的一部分,这将是出乎意料的。
因此,为了使这些选项按预期工作,read
内置程序需要告诉终端驱动程序在输入字符后立即返回。如果输入设备是终端,并且您指定了最大输入长度或换行符以外的分隔符,则通过修改以下termios 标志read
将终端置于“原始”模式:
off: ICANON INLCR OCRNL ONOCR ONLRET
on: ISIG IEXTEN ICRNL OPOST ONLCR
关闭后,ICANON
终端驱动程序不再缓冲输入。
如原始帖子中所述,Linux 内核驱动程序使用固定长度的 4096 输入缓冲区来实现行编辑,它会简单地忽略不适合此缓冲区的键入字符。因此,在终端处于正常输入模式时,您的输入将在 4096 个字符后被截断。关闭后,ICANON
驱动程序会尽快传递字符并且输入不会被截断。
但是关闭输入规范化的副作用是终端驱动程序不再解释退格键和删除键,从而无法进行行编辑。你可以试试这个:
# I typed a, x, backspace, b, return
$ read -n 4 input
ax^?b
$ printf "%s" "$input" | hd
00000000 61 62 7f 78 |ab.x|
00000004
请注意,退格键 ( 0x7f
) 发送的删除字符将保留在输入中。
这是一种不太理想的用户体验;您当然不希望它用于输入长输入。在大多数情况下,人们希望退格“起作用”。但是,它非常适合编写小型控制台游戏,其中脚本需要在键入时对每次击键做出反应。
Bash 本身使用该readline
库来读取输入。readline
也将终端置于原始模式,但与read
内置命令不同,它实际上处理退格字符、箭头键和大量其他字符,包括许多内核驱动程序显然一无所知的特殊字符,如制表符补全和历史搜索。
read
内置函数也有标志-e
,它会导致它使用readline
(如果它是从终端读取的)。进行上述实验-e
可能会产生更方便的结果:
# I typed a, x, backspace, b, c, d
$ read -en4 input
abcd
$ printf "%s" "$input" | hd
00000000 61 62 63 64 |abcd|
00000004
这一次,readline
处理了退格,并在read
输入四个“真实”字符后返回。