我发现subprocess.Popen()将跳过特定场景中的输入字节。为了证明这个问题,我写了以下(荒谬的)程序:
import sys
from subprocess import Popen
skip = int(sys.argv[1])
fin = sys.stdin
fin.read(skip)
cmd = 'wc -c'.split()
Popen(cmd, stdin=fin).wait()
该程序跳过指定的输入字节数,然后弹出wc
来计算剩余的字节数。
现在使用dd
试用程序来生成输入:
# skipping 0, everything works fine:
$ dd if=/dev/zero bs=1 count=100 2>/dev/null | python wc.py 0
100
$ # but skipping more than 0 yields an unexpected result.
$ # this should return 99:
$ dd if=/dev/zero bs=1 count=100 2>/dev/null | python wc.py 1
0
$ # I noticed it skips up to the 4k boundary.
$ # this should return 8191:
$ dd if=/dev/zero bs=1 count=8192 2>/dev/null | python wc.py 1
4096
任何人都可以解释这种意外行为吗?一个已知的问题?应该提交的错误? “你做错了吗?”
FWIW,我最后通过使用stdin管道绕过这个问题,然后一次输入一个数据块:
p = Popen(cmd, stdin=PIPE)
chunk = fin.read(CHUNK_SIZE)
while chunk:
p.stdin.write(chunk)
chunk = fin.read(CHUNK_SIZE)
p.stdin.close()
p.wait()
答案 0 :(得分:3)
.read()
上的sys.stdin
函数在Python中缓存。因此,当您读取一个字节时,Python实际上会读取整个缓冲区,期望您很快就会再次执行相同的操作。但是,读取缓冲区已满(在您的情况下为4096)意味着操作系统认为输入已被读取,并且不会将其传递给wc
。
您可以使用os.read()
跳过必要数量的输入字节来避免此问题。这会直接调用操作系统,不会在您的进程中缓冲数据:
os.read(fin.fileno(), skip)