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)