subprocess.Popen стандартный файл чтения

Я пытаюсь вызвать процесс для файла после того, как часть его была прочитана. Например:

with open('in.txt', 'r') as a, open('out.txt', 'w') as b:
  header = a.readline()
  subprocess.call(['sort'], stdin=a, stdout=b)

Это работает нормально, если я не читаю что-либо из a перед выполнением subprocess.call, но если я читаю что-либо из него, подпроцесс ничего не видит. Это использует Python 2.7.3. Я не могу найти ничего в документации, объясняющей это поведение, и (очень) краткий взгляд на источник подпроцесса не выявил причину.

3 ответа

Решение

Если вы открываете файл без буферизации, он работает:

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 Модуль работает на уровне файлового дескриптора (низкоуровневый небуферизованный ввод-вывод операционной системы). Это может работать с os.pipe(), socket.socket(), pty.openpty()что-нибудь с действительным .fileno() метод, если ОС поддерживает это.

Не рекомендуется смешивать буферизованный и небуферизованный ввод-вывод в одном и том же файле.

На 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,

По умолчанию bufsize а также bufsize=1 (строка с буферизацией) ведет себя аналогично на моей машине: начало файла исчезает - около 4 КБ.

file.tell() сообщает для всех размеров буфера положение в начале 2-й строки. С помощью next(file) вместо file.readline() приводит к file.tell() около 5 КБ на моей машине на Python 2 из-за ошибки буфера упреждающего чтения (io.open() дает ожидаемую позицию 2-й линии).

Попытка file.seek(file.tell()) прежде чем вызов подпроцесса не помогает в Python 2 с файловыми объектами по умолчанию на основе stdio. Работает с open() функции от io, _pyio модули на Python 2 и по умолчанию open (также ioна основе) на Python 3.

Попытка io, _pyio модули на Python 2 и Python 3 с и без file.flush() дает различные результаты. Это подтверждает, что смешивание буферизованного и небуферизованного ввода-вывода для одного и того же файлового дескриптора не является хорошей идеей.

Это происходит потому, что модуль подпроцесса извлекает дескриптор файла из объекта файла.

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

В строке 1126, начиная с 701.

Файловый объект использует буферы и уже прочитал много из дескриптора файла, когда подпроцесс извлекает его.

Я решил это в 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)

Другие вопросы по тегам