linux - bash:读取在 4096 字节后丢弃终端行输入

标签 linux bash terminal

为了演示这个问题,在 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 手册页中找到相关信息。这是我们可以依赖的功能吗?

最佳答案

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)发送的删除字符保留在输入中。

这是一种不太理想的用户体验;您当然不希望它用于输入长输入。在大多数情况下,人们希望退格键“起作用”。但是,它非常适合编写脚本需要在键入时对每个击键使用react的小型控制台游戏。

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

关于linux - bash:读取在 4096 字节后丢弃终端行输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52250059/

相关文章:

linux - 在 CVS 中是否可能 : "Clients not allowed to commit if there is no log written"?

c++ - 使用反引号时程序不输出

linux - 如何杀死dockerd -h fd//进程?

PHP 部署到 windows/unix 服务器

linux - 连续的 bash 脚本 - 进程终止时显示自定义错误

linux - 使用 SOCAT 记录在端口上收到的消息

python - 如何暂停在终端中运行的python脚本

regex - 使用 find 和 regex 区分 .h 和 .sh

php - 如何链接 mcrypt?

bash - Shell 函数字符串变量在 While 循环和 case 后结果为空?