为演示此问题,请在Linux中运行此命令后粘贴一个长字符串(> 4096字节):
read foo && wc -c <<<"$foo"
结果为4096,表示输入被截断。
Some research显示存在一个硬编码为4096的终端线缓冲区大小,这说明了截断。但是,当我尝试使用-n
选项阅读时,它起作用了:
read -n 32768 foo && wc -c <<<"$foo"
结果是输入的实际长度(+1,但这是由于这里的字符串)而不是4096。
所以我想知道选项-n 32768
的作用是什么。我没有在bash手册页中找到与此相关的信息。这是我们可以依靠的功能吗?
答案 0 :(得分:3)
Bash的read
实现允许您使用-n
标志指定要读取的最大字符数,或使用-d
标志指定替代的终止字符。这些选项均不适用于标准终端输入,因为通常,终端驱动程序会将输入保留在其自己的内部缓冲区中,直到用户键入ENTER键(或某些其他击键,如Control-C或Control-D)为止。
例如read -n1 char
背后的想法是,您希望read
在用户键入单个字符后立即返回,而不是希望read
等待用户键入完整的行,然后返回该行的第一个字符。同样,用户键入分号后,命令read -d';' command
应该立即返回;再次,等待用户键入完整的行,然后将其一部分返回到分号是意外的。
因此,为了使这些选项按预期工作,内置的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
处理了退格键,并且在键入四个“真实”字符后返回了read
。
答案 1 :(得分:0)
从bash联机帮助页上阅读:
-n个nchars
read在读取nchars个字符后返回,而不是等待完整的输入行,但是如果在分隔符之前读取的字符数少于nchars个,则使用分隔符。
(我很确定这是一个bash特定的扩展,如果您使用其他shell,除非您确认某个特定的扩展也支持它,否则您不能依赖它)。编辑:例如,zsh与-n
的功能截然不同。