Bash的read实现使您可以使用-n标志来指定要读取的最大字符数,或者使用-d标志来指定替代的终止字符.这些选项均不适用于标准终端输入,因为通常,终端驱动程序会将输入保留在其自己的内部缓冲区中,直到用户键入ENTER键(或某些其他击键,如Control-C或Control-D)为止.
例如,读取-n1 char背后的想法是,您希望在用户键入单个字符后立即返回读取,而不是希望读取等待用户键入完整的行然后返回第一个字符该行的字符.同样,命令读为-d’;’用户键入分号后,命令应立即返回;再次,等待用户键入完整的行,然后仅将其一部分返回到分号是意外的.
因此,为了使这些选项按预期方式工作,内置的read需要告诉终端驱动程序,在键入字符后立即返回它们.如果输入设备是终端,并且您指定了最大输入长度或除换行符以外的定界符,则read通过修改以下termios flags将终端置于“原始”模式:
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处理了退格键,并在键入四个“真实”字符后返回读取结果.