В Windows как открыть на запись файл, уже открытый на запись другим процессом?

Я пытаюсь открыть файл журнала, открытый другим процессом, и удалить первые несколько строк. В Unix я бы просто сделал os.open('/tmp/file.log', os.O_NONBLOCK), и это приблизило бы меня к моей цели.

Теперь я застрял в Windows, и мне нужно как-то повернуть этот журнал, не закрывая приложение, содержащее файл. Это вообще возможно?

Сначала я подумал об открытии дескриптора файла в том месте, где приложение ожидало журнал, и просто действовать как канал в дескриптор файла в Python, но я не смог найти никакого способа сделать это в Windows.

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

Думал и о O_SHLOCK, но опять же, это Unix, а не Windows. Поэтому я выбрал mmap файл и надеялся, что это сделает его немного более гибким, но это ни к чему меня не привело.

import mmap
import contextlib
import time

with open(r'test.log', 'r+') as f:
    with contextlib.closing(mmap.mmap(f.fileno(), 0)) as m:
        while 1:
            line = m.readline()
            if len(line) > 0:
                print line
            time.sleep(0.5)

Это приводит к тому, что приложение не может получить доступ к файлу, потому что Python удерживает его (и наоборот).

Пришел к мысли о signal.SIGHUP, но его тоже нет в Windows, так что вернемся к исходной точке.

Я застрял, и я пробовал все это, может ли Python помочь мне здесь или мне нужно переключить язык?


person Torxed    schedule 13.08.2013    source источник
comment
Какой питон - 2 или 3?   -  person Piotr Dobrogost    schedule 14.08.2013
comment
2.6. Извините, что не пояснил это раньше.   -  person Torxed    schedule 14.08.2013
comment
Обратите внимание, что флаг O_NONBLOCK не имеет ничего общего с открытием одного и того же файла более чем одним процессом одновременно.   -  person Piotr Dobrogost    schedule 15.07.2016


Ответы (2)


Даже если приложение открывает файл как общий объект, Python не может, так что они не могут ужиться, судя по всему.

Это не так плохо :). Вы можете (должны) открыть файл, используя CreateFile, как указал Аугусто. Для этого можно использовать стандартный модуль ctypes. В вопросе Использование структуры в качестве аргумента функции с модулем ctypes python вы можете увидеть, как это сделать. Затем вам нужно связать дескриптор файла времени выполнения C с существующим дескриптором файла операционной системы, который вы получили на предыдущем шаге. Вы можете использовать _open_osfhandle из Библиотека времени выполнения MS C (CRT), чтобы сделать это. Вы можете вызвать его еще раз, используя ctypes; вы можете получить к нему доступ как ctypes.cdll.msvcrt._open_osfhandle. Затем вам нужно связать файловый объект Python с существующим дескриптором файла времени выполнения C, который вы получили на предыдущем шаге. Чтобы сделать это в Python 3, вы просто передаете файловый дескриптор в качестве первого аргумента встроенной функции open функция. Согласно документам

file — это либо строковый, либо байтовый объект, задающий путь (абсолютный или относительный к текущему рабочему каталогу) открываемого файла или целочисленный файловый дескриптор файла, который нужно обернуть.

В Python 2 вы должны использовать os.fdopen; его задача, согласно документам, состоит в том, чтобы

Возвращает открытый файловый объект, связанный с файловым дескриптором fd.

Все вышеперечисленное не должно требоваться для выполнения такой простой вещи. Есть надежда, что это будет намного проще, когда реализация CPython в Windows начнет использовать собственный API Windows для файлов вместо использования библиотеки времени выполнения C, которая не дает доступа ко многим функциям платформы Windows. Дополнительные сведения см. в разделе Добавление нового io.FileIO с помощью собственного Windows API.

person Piotr Dobrogost    schedule 15.08.2013
comment
Отлично, на самом деле я сделал более уродливую версию, в которой я обернул приложение в подпроцесс, выловил стандартный вывод и отправил его на свой сервер syslog-ng. Но я заменю свой уродливый хак этим как можно скорее! Дружище, как раз то, что мне было нужно! - person Torxed; 16.08.2013

Есть ли у вас какой-либо контроль над приложением, создающим файл журнала? Потому что в зависимости от того, как файл открывается этим приложением, вы действительно не можете его изменить.

Эта ссылка может показаться здесь не по теме, но глубоко в Windows то, что определяет доступ к файлу для другого приложения, — это параметр dwShareMode функции CreateFile: http://msdn.microsoft.com/en-us/библиотека/окна/рабочийстол/aa363858%28v=vs.85%29.aspx

Приложение должно включить FILE_SHARE_WRITE и, возможно, FILE_SHARE_DELETE, плюс оно должно сбрасывать и обновлять позицию файла каждый раз, когда оно записывает файл. Глядя на документацию Python для open(), такого подробного параметра нет.

person augustomen    schedule 13.08.2013
comment
Прежде всего, спасибо! Во-вторых, я упомянул os.open(), который сам по себе является более ручным способом выполнения только open(), и, как также упоминалось, в Windows отсутствует опция SHARE (что является ошибкой). Даже если приложение открывает файл как общий объект, Python не может, так что они не могут ужиться, судя по всему. Также я не могу изменить исходное приложение (код C) больше, чем включить общий флаг при открытии файла журнала. - person Torxed; 13.08.2013
comment
См. Как создать временный файл, который может быть прочитан подпроцессом?. Там есть ссылка на tempfile.NamedTemporaryFile, не особенно полезный в Windows Ошибка Python, в которой есть интересное обсуждение открытия один и тот же файл несколькими процессами в Windows. - person Piotr Dobrogost; 17.07.2016