Linux终端输入:从终端读取用户输入,将行截断限制在4095个字符以内。

19
在一个bash脚本中,我尝试从标准输入读取行,使用内置的read命令并设置IFS=$'\n'。如果我将输入粘贴到read中,行会被截断为4095个字符。这种限制似乎来自于从终端读取,因为这种方法完全正常:
fill=
for i in $(seq 1 94); do fill="${fill}x"; done
for i in $(seq 1 100); do printf "%04d00$fill" $i; done | (read line; echo $line)

我用 Python 脚本也遇到了同样的问题(无法从终端接受超过 4095 字节的输入,但可以从管道接受)。
#!/usr/bin/python

from sys import stdin

line = stdin.readline()
print('%s' % line)

即使是C程序,使用read(2)也是一样的:

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    char buf[32768];
    int sz = read(0, buf, sizeof(buf) - 1);
    buf[sz] = '\0';
    printf("READ LINE: [%s]\n", buf);
    return 0;
}

在所有情况下,我不能输入超过大约4095个字符。输入提示停止接受字符。

问题1:是否有办法在Linux系统(至少Ubuntu 10.04和13.04)中从终端交互式读取超过4095个字符的内容?

问题2:这个限制来自哪里?

受影响的系统:我注意到这个限制在Ubuntu 10.04/x86和13.04/x86中存在,但是Cygwin(最近版本至少)在超过10000个字符时仍不截断(由于我需要让这个脚本在Ubuntu中工作,所以没有进一步测试)。使用的终端:Virtual Console和KDE konsole(Ubuntu 13.04)和gnome-terminal(Ubuntu 10.04)。

3个回答

12
请参阅termios(3)手册页面,在"Canonical and noncanonical mode"章节下。通常,终端(标准输入)处于规范模式;在这种模式下,内核将缓冲输入行,然后将输入返回给应用程序。对于Linux来说,硬编码限制(N_TTY_BUF_SIZE定义在${linux_source_path}/include/linux/tty.h)设置为4096,允许输入4095个字符,不包括结束的新行。您还可以查看文件${linux_source_path}/drivers/tty/n_tty.c、函数n_tty_receive_buf_common()及其上面的注释。
In noncanonical mode, 默认情况下内核不进行缓冲,read(2)系统调用一旦返回单个输入字符(键按下)即时返回。您可以操作终端设置以读取指定数量的字符或设置非规范模式的超时时间,但是硬编码限制仍为4095,根据termios(3)手册页(以及上述n_tty_receive_buf_common()上面的注释)。
Bash read内置命令仍在非规范模式下工作,如下所示:
IFS=$'\n'      # Allow spaces and other white spaces.
stty -icanon   # Disable canonical mode.
read line      # Now we can read without inhibitions set by terminal.
stty icanon    # Re-enable canonical mode (assuming it was enabled to begin with).

在添加了 stty -icanon 的修改之后,您可以成功地使用 bash 内置的 read 命令粘贴超过4096个字符的字符串并读取它(我已经成功尝试了超过10000个字符)。

如果您将此放入文件中,即使将其设为脚本,也可以使用 strace 查看调用的系统调用,并且您会看到每次调用 read(2) 时,当您为其输入数据时,每次都返回一个字符。


Unix & Linux 上有一个更新的问题,源于有人遇到了同样的限制。我从这个很棒的回答中借鉴了 Linux 内核代码的参考链接:https://unix.stackexchange.com/a/643783/19201 - FooF

-1

很抱歉我没有解决方案可以提供给您,但我可以回答问题2。 在Linux中,PIPE_BUF被设置为4096(在limits.h中)。如果您向管道写入超过4096的数据,则会被截断。

来自/usr/include/linux/limits.h

#ifndef _LINUX_LIMITS_H
#define _LINUX_LIMITS_H

#define NR_OPEN         1024

#define NGROUPS_MAX    65536    /* supplemental group IDs are available */
#define ARG_MAX       131072    /* # bytes of args + environ for exec() */
#define LINK_MAX         127    /* # links a file may have */
#define MAX_CANON        255    /* size of the canonical input queue */
#define MAX_INPUT        255    /* size of the type-ahead buffer */
#define NAME_MAX         255    /* # chars in a file name */
#define PATH_MAX        4096    /* # chars in a path name including nul */
#define PIPE_BUF        4096    /* # bytes in atomic write to a pipe */
#define XATTR_NAME_MAX   255    /* # chars in an extended attribute name */
#define XATTR_SIZE_MAX 65536    /* size of an extended attribute value (64k) */
#define XATTR_LIST_MAX 65536    /* size of extended attribute namelist (64k) */

#define RTSIG_MAX     32

#endif

是的,管道确实存在这样的限制,实际上需要注意的是非交互版本使用管道,必然会有多次写入和读取。但我认为这个限制不应该影响终端输入(终端不是管道)。 - FooF

-3

问题绝对不是read()函数,因为它可以读取任何有效的整数值。问题来自堆内存或管道大小...因为它们是大小的唯一可能限制因素。


不,问题在于终端规范模式下的缓冲区大小。输入行为4096(最后一个字节保留为换行符)。原问题的提问者在答案中已经解释和演示了这一点。他说答案不完整是因为他还没时间编写一个C程序来演示,并且他无法指出内核中定义了这个限制的位置。 - FooF
这就是我想说的,管道缓冲区大小为4096。可以使用“ulimit -p”进行检查,它将返回8作为答案,这意味着8 * 512字节= 4096。由于每个字符是1个字节,因此它只读取4095个字节和最后一个字符作为换行符,就像你所说的那样。 - Abhishek
1
我认为这是一个不同的常量。恰好4096是默认页面大小,这就解释了为什么在规范模式下最大管道大小和终端输入缓冲区的值恰好相同。 - FooF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接