我尝试在读取部分文件后调用文件上的进程。例如:
with open('in.txt', 'r') as a, open('out.txt', 'w') as b:
header = a.readline()
subprocess.call(['sort'], stdin=a, stdout=b)
如果我在执行subprocess.call之前没有读取任何内容,这可以正常工作,但如果我从中读取任何内容,则子进程看不到任何内容。这是使用python 2.7.3。我无法在文档中找到解释此行为的任何内容,并且(非常)简要地浏览一下子流程源并没有发现原因。
答案 0 :(得分:9)
如果您打开未缓冲的文件,那么它可以正常工作:
import subprocess
with open('in.txt', 'rb', 0) as a, open('out.txt', 'w') as b:
header = a.readline()
rc = subprocess.call(['sort'], stdin=a, stdout=b)
subprocess
模块在文件描述符级别(操作系统的低级无缓冲I / O)工作。如果操作系统支持,它可以与os.pipe()
,socket.socket()
,pty.openpty()
一起使用有效.fileno()
方法。
建议不要在同一个文件中混用缓冲和非缓冲的I / O.
在Python 2上,file.flush()
导致输出显示为:。
import subprocess
# 2nd
with open(__file__) as file:
header = file.readline()
file.seek(file.tell()) # synchronize (for io.open and Python 3)
file.flush() # synchronize (for C stdio-based file on Python 2)
rc = subprocess.call(['cat'], stdin=file)
如果没有带有subprocess
的{{1}}模块,则可以重现该问题:
os.read()
如果缓冲区大小很小,则打印文件的其余部分:
#!/usr/bin/env python
# 2nd
import os
with open(__file__) as file: #XXX fully buffered text file EATS INPUT
file.readline() # ignore header line
os.write(1, os.read(file.fileno(), 1<<20))
如果第一行大小不能被#!/usr/bin/env python
# 2nd
import os
bufsize = 2 #XXX MAY EAT INPUT
with open(__file__, 'rb', bufsize) as file:
file.readline() # ignore header line
os.write(2, os.read(file.fileno(), 1<<20))
整除,则会输入更多输入。
默认bufsize
和bufsize
(行缓冲)在我的机器上表现相似:文件的开头消失 - 大约4KB。
bufsize=1
报告所有缓冲区大小在第二行开头的位置。由于read-ahead buffer bug(file.tell()
给出了预期的第二行位置),因此在我的机器上使用next(file)
而不是file.readline()
导致file.tell()
大约5K。
在子进程调用之前尝试io.open()
对于使用默认的基于stdio的文件对象的Python 2没有帮助。它适用于Python 2上的file.seek(file.tell())
,open()
模块中的io
函数以及Python 3上的默认_pyio
(也基于open
)。
使用和不使用io
在Python 2和Python 3上尝试io
,_pyio
模块会产生各种结果。它确认在同一文件描述符上混合缓冲和非缓冲I / O不是一个好主意。
答案 1 :(得分:4)
这是因为子进程模块从文件对象中提取文件句柄。
http://hg.python.org/releasing/2.7.6/file/ba31940588b6/Lib/subprocess.py
第1126行,来自701。
文件对象使用缓冲区,并且在子进程提取时已经从文件句柄中读取了很多内容。
答案 2 :(得分:0)
我通过对齐文件描述符位置在python 2.7中解决了这个问题。
os.lseek(_file.fileno(), _file.tell(), os.SEEK_SET)
truncate_null_cmd = ['tr','-d', '\\000']
subprocess.Popen(truncate_null_cmd, stdin=_file, stdout=subprocess.PIPE)