如何在终端上没有换行的情况下读取4k输入?


25

因此我在剪贴板上有很多数据而没有新行(一行上是一个很大的SVG文件)。我去了

$ cat >file.svg

然后尝试粘贴(在Gnome终端中),但只接受前4kB个字符。

我认为这是一个readline功能/局限性。

有没有一种可以从STDIN读取的方法来避免此问题?

编辑

测试用例:创建一个演示文件。这个将带有〜4k“ =”符号,后跟“ foo bar”。

{ printf '=%.0s' {1..4095} ; echo "foo bar" ; } > test.in

将其复制到剪贴板

xclip test.in

(如果要单击鼠标中键以插入)或

xclip -selection clipboard test.in

(如果您想使用Ctrl-Shift-Insert粘贴它)

然后cat >test.out,粘贴(以任何一种方式)。按Ctrl-D结束流。cat test.out-您看到“ foo bar”了吗?

在我的设置(Ubuntu 12.04,Gnome Terminal,zsh)上,粘贴时,我只会看到,=而看不到foo bar。我检查的时候也一样test.out


您确定您的SVG文件已完全读入剪贴板吗?
lgeorget 2014年

你的实际问题是什么?如何将剪贴板的内容存储到文件中?如果是这样,除了在终端中粘贴外,还有其他方法。
lgeorget 2014年

您的情况下N是多少?我已经尝试了2kB的xml数据(inc LF),没有问题。
fduff 2014年

1
@artfulrobot前台进程直接与tty / pty交互。不涉及外壳。之所以会看到这种情况,是因为如果程序本身不使用readline或任何其他输入库,则它们在程序中没有readline功能(编辑/跳转命令,历史记录...)。
jofel 2014年

1
这不是readline的限制-这里不涉及readline和bash。这是终端接口的限制。
吉尔(Gilles)'所以

Answers:


22

如果我正确理解了源代码,那么在Linux下,一次在终端上可以读取的最大字符数取决于N_TTY_BUF_SIZE内核源代码。该值为 4096。

这是终端界面的限制,特别是规范(“ cooked”)模式提供了非常粗糙的行编辑器(退格,回车,在文件末尾的行首输入Ctrl+ D)。它完全发生在读取过程之外。

您可以将终端切换到原始模式,这将禁用线路处理。它还禁用Ctrl+ D和其他功能,给您的程序增加了负担。

这是一个古老的Unix限制,由于动机很少,因此从未得到解决。人类不会排长队。如果要从程序中输入输入,则可以从文件或管道中重定向程序的输入。

例如,要使用X剪贴板的内容,请使用xsel或的管道xclip。在您的情况下:

xsel -b >file.svg
xclip -selection clipboard >file.svg

删除-b-selection clipboard使用X选择(使用鼠标突出显示设置的X选择)而不是剪贴板。

在OSX上,用于pbpaste粘贴剪贴板内容(并pbcopy进行设置)。

如果激活X11转发ssh -X(某些服务器可能禁止),则可以通过SSH访问X剪贴板。如果只能在ssh不使用X11转发的情况下使用scp,则可以使用sftpsshfs复制文件。

如果粘贴是唯一的解决方案,因为您无法转发剪贴板,或者您无法粘贴,例如,伪造键入虚拟机,则另一种方法是将数据编码为带有换行符的内容。Base64非常适合此:将任意数据转换为可打印字符,并在解码时忽略空格。这种方法的另一个优点是,它支持输入中的任意数据,甚至支持终端粘贴时会解释的控制字符。您可以对内容进行编码:

xsel -b | base64 | xsel -b

然后将其解码:

base64 -d
 Paste
Ctrl+D

请注意,使用xsel> 4k字节使用时,确实存在一个令人讨厌的数据损坏错误:github.com/kfish/xsel/issues/14
Patrick

14

您正在运行到该限制是在一条线上的最大尺寸规范输入模式MAX_CANON

在规范输入模式下,tty驱动程序提供了基本的行编辑服务,因此用户空间程序不需要这样做。它不具有与readline一样多的功能,但是它可以识别一些可配置的特殊字符,例如擦除(通常为Backspace或Delete)和kill(通常为Ctrl-U)。

对于您的问题最重要的是,规范模式会缓冲输入,直到看到行尾字符。由于缓冲区位于tty驱动程序中,因此在内核内存中并不是很大。

您可以使用stty cbreak或来关闭规范模式stty -icanon,然后粘贴。这有一个很大的缺点,即您将无法使用Ctrl-D发送EOF。这是规范模式所负责的另一件事。您仍然可以cat使用Ctrl-C 终止,因为产生信号的字符由单独的标志(stty rawstty -isig)控制。

对我来说,神秘的原因是,由于您已经证明自己了解xclip,所以您不只是使用xclip -o > file来代替cat


1
这个谜团可以轻松解决:似乎artfulrobot希望使用剪贴板中的数据快速填充远程主机上的文件。在远程外壳程序中,通常无法通过xclip直接访问本地剪贴板。
2014年

3
嗯,旧的粘贴粘贴效果不错。如果我必须执行其中之一而不是纯文本格式,那么我会对其进行编码,而不是试图说服tty驱动程序通过它。带有大量行的纯文本也可以这样处理。

2

如果您这样做:

stty eol =

然后运行EDIT中建议的演示,您将在test.out的打印输出中看到foo bar。终端的线路规则将在读取输入中的每个特殊eol字符时将其输出刷新到其读取器。

Linux规范模式终端-可以配置stty icanon或可能仅配置为stty sane-处理以下特殊输入字符...

  • of
    • 默认: ^D
    • 终止输入线,并将输出刷新到阅读器。因为它已从输入中删除,所以如果将它作为一行中的唯一字符输入,则它将作为读取(或文件结尾)传递给读取器。
  • EOL
    • 默认值:未分配
    • 也终止输入线,但不会从输入中删除。
    • 默认: ^U
    • 擦除所有缓冲的输入。
  • 抹去
    • 默认:^H (或可能@^?在某些系统上)
    • 擦除最后一个缓冲的输入字符。

当还设置了iexten时 -类似,stty icanon iexten或者再次可能是stty sane,一个规范的Linux终端也将处理...

  • eol2
    • 默认值:未分配
    • 终止的输入线,并且不会从输入移除。
    • 默认: ^W
    • 擦除最后一个缓冲的输入
  • pr
    • 默认: ^R
    • 重新打印所有缓冲的输入。
  • 下一个
    • 默认: ^V
    • 就紧随其后的输入字符的行纪律而言,删除任何特殊意义。

通过从输入流中删除它们来处理这些字符(eoleol2除外),即在将处理后的流传递给读取器之前执行相关的特殊功能-通常是您的shell,但是无论前台进程组是什么。

其他特殊输入字符也经过类似处理,但可以独立于任何icanon设置进行配置,包括isig设置-类似的设置stty isig,也可能包含在合理的配置中:

  • 退出
    • 默认: ^\
    • 刷新所有缓冲的输入(如果未设置noflsh),然后将SIGQUIT发送到前台进程组-可能会生成核心转储。
  • 悬念
    • 默认: ^Z
    • 刷新所有缓冲的输入(如果未设置noflsh),然后将SIGTSTP发送到前台进程组。暂停的进程组很可能可以使用作业控制的shell 中的一个kill -CONT "$!"或仅fg在其中进行恢复。set -m
  • 内部
    • 默认: ^C
    • 刷新所有缓冲的输入(如果未设置noflsh),然后将SIGINT发送到前台进程组。

ixon集-配置类似stty ixon并且通常也包含在合理的配置中:

    • 默认: ^S
    • 停止所有输出到读取器,直到在输入中读取了start或- 也设置了ixany-至少读取了一个字符为止。
  • 开始
    • 默认: ^Q
    • 如果先前已使用stop停止输出,则重新启动输出。
  • 两者的停止开始从输入移除处理时,但重新启动,如果输出由于在输入任何字符时IXANY被设置,那么该字符不会被删除。

在其他非Linux系统上处理的特殊字符可能包括...

  • 齐平
    • 默认: ^O
    • 切换丢弃和刷新缓冲的输入并将其从输入中移除。
  • Dsusp
    • 默认值:未分配
    • 仅当阅读器读取分配的特殊输入字符然后发送SIGTSTP时,才刷新所有缓冲的输入。

可能...

  • 开关
    • 默认值^@ (含义\0NUL
    • 切换前景外壳层。与某些系统上的shl Shell-layers应用程序一起使用。
    • 可以在工具套件中自由地实现shl复用pty并因此与作业控制兼容的实现,而不是原始实现依赖于swtch的行为heirloom-toolchest

为了更清楚地了解如何以及为什么(也许为什么不)处理这些输入函数,请咨询man 3 termios

上述所有功能都可以分配(或重新分配)(如适用sttyfunction assigned-key。要禁用任何单个功能,请执行。另外,与任何与所有GNU,AST,或传家宝的前述的行编辑功能分配各种尝试实现似乎表明,你也可以为NUL分配任何功能似乎等同于将其设置为未分配在我的Linux系统。sttyfunction^-sttysttyfunction^@

也许在键入这些字符时确实看到了这些字符的回显(可以使用[-] ctlecho进行配置),但这只是一个标记,用来向您显示操作的位置-接收到输入的程序并不认为您输入它们eol [2]除外),并且仅接收行规对其应用了效果的输入副本。

终端处理各种行编辑功能的结果是,它必须在某种程度上缓冲输入,以便对您指示它应该执行的功能采取行动-因此,不能无限制地提供输入你可能随时杀人。该缓冲是更精确的缓冲区。

如果设置了EOLEOL2字符发生在输入一些分隔符-即使既不是一个新行或回车符,例如-那么你将只能了到如此地步,它上次发生和你缓冲区将尽可能地扩展到输入中的下一个-或换行符(如果设置了icrnl而没有设置igncr,则返回一个换行符


1

cat可以接受任意数量的字符,例如您可以通过这样做来见证cat /dev/random > test.bin(除非您知道如何停止它,否则不要这样做:)。我尝试将大文件复制并粘贴到中cat > test.txt。无论我用Ctrl- cCtrl- 取消,所有行都以文件结尾d,但是在前一种情况下,并非所有行都被打印到终端。我相信这是因为要cat缓冲其打印,在每次打印之前都要等待完整的文本缓冲或从终端直接输入。

在我的系统上,我认为缓冲区大小为4096(2 ^ 12)字节:使用创建一个4095字节的文件,使用(printf '1234567890%.0s' {1..409} && printf 12345) > test.in-将其加载到复制缓冲区中xclip test.incat > test.out使用Shift- 开始,粘贴Insert,然后按Ctrl- 终止流d。现在,使用添加一个字节printf '6' >> test.in,流将被打印两次:一次在cat输出中(全部4096字节),最后4095个字节在终止后再次在shell上。


+1就我而言,这还取决于所使用的剪贴板。如果我使用选择缓冲区(单击中键粘贴),则只能看到测试数据的前4542行(但所有行最终都在创建的文件中),但是使用的是X剪贴板(Ctrl + C / Ctrl + V)所有的。在这两种情况下,所有数据都被打印到结果文件中,但是在前一种情况下,终端中仅显示了部分数据。
terdon

1
我没有相同的行为。看到编辑过的问题
artfulrobot 2014年

0

一种解决方案是将其粘贴到支持长行的编辑器中,例如vim。

如果使用vim,请先使用进入粘贴模式,:paste然后再使用i并粘贴文本进入插入模式。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.