3

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にパイプを使用して問題を回避し、一度に1つのチャンクでデータをフィードすることになりました。

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()
4

1 に答える 1

3

上の.read()関数sys.stdinはPython内にバッファリングされます。したがって、バイトを読み取るとき、Pythonは実際にはバッファー全体を読み取り、すぐに同じことを再度実行することを期待しています。ただし、バッファがいっぱいになると(状況によっては4096)、OSは入力がすでに読み取られていると見なし、に渡さないことを意味しwcます。

os.read()を使用して必要な入力バイト数をスキップすることにより、この問題を回避できます。これにより、OSが直接呼び出され、プロセス内のデータはバッファリングされません。

os.read(fin.fileno(), skip)
于 2012-12-26T18:34:41.187 に答える