这种将长行截断为4096字节的行为是由Linux内核中的终端(TTY)代码引起的.(实际上,作为截断的一部分,4096字节的最后一个字节也被换行字节替换.)当(Python)进程从TTY读取作为其标准输入时,该行已经被截断.对于您的用例,没有简单的修复方法,即防止在将较长的行复制粘贴到终端窗口时被截断.作为一种解决办法,可以将其复制粘贴到文件(例如infile.dat
),然后运行python script.py <infile.dat
.
即使没有Python,也很容易重现截断行为,只需运行dd bs=65536 of=/dev/null
,复制粘贴一行超过4096个字节,然后按Ctrl-D来表示EOF.输出的最后一行将以4096 bytes (4.1 kB, 4.0 KiB) copied,
开始,表示只读取了4096个字节.如果复制粘贴多个长行,您将看到每行都将被截断为4096字节(包括换行字节).
有关此Linux内核行为的更多分析:
字节数限制4096在Linux内核中被硬编码为N_TTY_BUF_SIZE.
我回答的其余部分演示了如何在不截断的情况下使用Python和shell 程序(例如Bash),因此它们不会导致问题.
这是为了证明Pythonsys.stderr.readline()
不会截断,因此不需要更改您的Python代码.
Python sys.stdin.readline()
有一个无限的缓冲区(假设有足够的空闲内存).我已经在Linux上用Python 2.7、3.6和更新的Pythontry 过了.
以下是我try 过的:
立即从管道中读取较短的行(在Python中没有额外的缓冲延迟):
$ (echo -n A; sleep .3; echo a; sleep .3; echo B; sleep .3) | python -c "if 1:
for line in iter(__import__('sys').stdin.readline, ''): print([line])"
['Aa\n']
['B\n']
要try 它,请运行不带前导$
的命令.它适用于Linux,我认为它将适用于macOS,Windows和其他系统.在Windows上,您可能需要删除if 1:
和换行符.
在Python3.x中,使用sys.stdin.buffer.readline()
立即从管道中将短行作为字节(而不是Unicode字符)读取:
$ (echo -n A; sleep .3; echo a; sleep .3; echo B; sleep .3) | python -c "if 1:
for line in iter(__import__('sys').stdin.buffer.readline, b''): print([line])"
[b'Aa\n']
[b'B\n']
要try 它,请运行不带前导$
的命令.它适用于Linux,我认为它将适用于macOS,Windows和其他系统.在Windows上,您可能需要删除if 1:
和换行符.
立即读取一个长(超过10 MiB)行,而不从管道截断:
$ python -c "if 1:
import sys, time; f = sys.stdout
f.write('A' * 10987654); f.flush(); time.sleep(.3)
f.write('aaa\n'); f.flush(); time.sleep(.3)
f.write('B\n'); f.flush(); time.sleep(.3)" |
python -c "if 1:
for line in iter(__import__('sys').stdin.readline, ''): print(len(line))"
10987658
2
要try 它,请运行不带前导$
的命令.对我来说,它可以在Linux上运行,我认为它也可以在MacOS、Windows和其他系统上运行.在Windows上,将Python代码放到文件a.py
和b.py
中,然后运行python a.py | python b.py
.
在某些情况下,尽可能快地(即进程一接收到输入)将输入传递给Python程序是很有用的,也就是防止在sys.stdin
中缓冲导致的延迟.
好消息是:sys.stdin.readline()
在进程可用时立即返回下一行,而不是等待后续行.在循环中,使用for line in iter(sys.stdin.readline, ''):
,不要使用for line in sys.stdin:
,因为即使有行可用,for line in sys.stdin:
也会等待更多的输入.有关详细信息,请参阅https://stackoverflow.com/a/28919832/97248和其他答案.
sys.stdin.read(n)
通常具有缓冲延迟:即使进程已经读取了n
个字节,sys.stdin.read(n)
也将等待更多字节,直到其缓冲区(通常为8192个字节)被填满.为了避免在Python3中出现这种延迟,请改用sys.stdin.buffer.raw.read(n)
.它将最多读取n
个字节(not个Unicode字符),并且只要有至少一个字节可用,它就会立即返回.不要把它和sys.stdin.readline()
混在一起.在Python2和3中,使用os.read(sys.stdin.fileno(), n)
实现这一点.使用管道(例如cat | python ...
)测试缓冲延迟,因为如果没有管道,系统可能会使用终端(TTY)设备,该设备在默认情况下具有线路缓冲,在行尾更早地返回数据.
这是为了证明导致截断的不是shell .以下是操作方法:
在您的常规Linux图形用户界面上,运行以下任一命令(不包括前$
):
$ xterm -e dd bs=1 status=progress
$ konsole -e dd bs=1 status=progress
$ gnome-terminal -- dd bs=1 status=progress
将出现一个新的空终端窗口.
在另一个程序中,将一行长度超过4096字节的代码复制到剪贴板(或者,你可以复制一个包含多行的文本,有些长,有些短).
Paste it to the new, empty terminal window. If unsure, press Shift-Insert to paste. If that doesn't work, use Ctrl-Shift-V to paste. If that doesn't work either, use Edit / Paste in the menu.
等一下,在窗口中按Enter.dd将显示类似... bytes (...) copied, ...的内容.字节数将小于预期,表示行截断为4096个字节.
你现在可以关上windows 了.或者只需按Ctrl-C或Ctrl-D退出dd,导致窗口关闭.
这个演示已经证明,即使没有shell (或Python进程),也会发生截断.