bash:读取4096字节后丢弃终端线路输入

问题描述 投票:3回答:2

要演示此问题,请在Linux中运行此命令后粘贴长字符串(> 4096字节):

read foo && wc -c <<<"$foo"

结果是4096,这意味着输入被截断。

Some research透露,终端线缓冲区大小硬编码为4096,这解释了截断。但是,当我尝试使用-n选项阅读时,它有效:

read -n 32768 foo && wc -c <<<"$foo"

结果是输入的实际长度(+1,但这是由于here-string)而不是4096。

所以我想知道选项-n 32768的魔力是什么。我没有在bash手册页中找到关于此的相关信息。这是我们可以依赖的功能吗?

linux bash terminal
2个回答
3
投票

Bash的read实现允许您使用-n标志指定要读取的最大字符数,使用-d标志或替代终止字符。这些选项都不适用于标准终端输入,因为通常终端驱动程序将输入保持在其自己的内部缓冲区中,直到用户键入ENTER键(或某些其他键击,如Control-C或Control-D)。

例如,read -n1 char背后的想法是,你希望read在用户输入单个字符后立即返回,而不是你希望read等待用户输入一个完整的行,然后返回该字符的第一个字符线。同样,命令read -d';' command应在用户键入分号后立即返回;再次,等待用户键入一个完整的行,然后只是将它的一部分返回到分号将是意外的。

因此,为了使这些选项按预期工作,read builtin需要告诉终端驱动程序在键入后立即返回字符。如果输入设备是终端,并且您指定了最大输入长度或除换行符之外的分隔符,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在输入四个“真实”角色后返回。


0
投票

从bash手册页上阅读:

-On ncars

read读取nchars字符后返回,而不是等待完整的输入行,但如果在分隔符之前读取的字符数少于nchars,则表示分隔符。

(我很确定这是一个特定于bash的扩展,如果使用其他shell,你不能依赖它,除非你验证特定的一个也支持它)。编辑:例如,zsh与-n做了一些非常不同的事情。

© www.soinside.com 2019 - 2024. All rights reserved.