子进程.Popen stdin read fi

2024-05-23 19:22:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在文件的一部分被读取后调用该进程。例如:

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之前我没有从a中读取任何内容,那么这很好,但是如果我从中读取了任何内容,那么子流程就看不到任何内容。这是使用Python2.7.3。我在文档中找不到解释这种行为的任何东西,对子流程源代码的(非常)简短的一瞥并没有揭示原因。


Tags: 文件intxt内容readline进程aswith
3条回答

发生这种情况是因为子流程模块从文件对象中提取文件句柄。

http://hg.python.org/releasing/2.7.6/file/ba31940588b6/Lib/subprocess.py

第1126行,来自701。

文件对象使用缓冲区,并且在子进程提取它时已经从文件句柄中读取了很多内容。

我在Python2.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)

如果未缓冲地打开文件,则可以:

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模块和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整除,它将消耗更多的输入。

默认的bufsizebufsize=1(行缓冲)在我的机器上的行为类似:文件的开头消失了——大约4KB。

file.tell()为所有缓冲区大小报告第2行开头的位置。使用next(file)而不是file.readline()会导致file.tell()在Python 2上,由于read-ahead buffer bugio.open()给出了预期的第二行位置),所以在我的机器上大约有5公里。

在调用子进程之前尝试file.seek(file.tell())对于使用默认的基于stdio的文件对象的Python 2没有帮助。它与Python 2上的io_pyio模块中的open()函数以及Python 3上的默认open(也基于io)一起工作。

在Python 2和Python 3上尝试io_pyio模块时,无论是否使用file.flush(),都会产生各种结果。它证实了在同一个文件描述符上混合缓冲和非缓冲I/O不是一个好主意。

相关问题 更多 >