簡體   English   中英

如何創建一個子進程可以讀取的臨時文件?

[英]How to create a temporary file that can be read by a subprocess?

我正在編寫一個 Python 腳本,該腳本需要將一些數據寫入臨時文件,然后創建一個運行 C++ 程序的子進程,該程序將讀取臨時文件。 我正在嘗試為此使用NamedTemporaryFile ,但根據文檔,

在命名的臨時文件仍處於打開狀態時,該名稱是否可用於第二次打開文件,因平台而異(在 Unix 上可以這樣使用;在 Windows NT 或更高版本上不能)。

事實上,在 Windows 上,如果我在寫入后刷新臨時文件,但在我希望它消失之前不要關閉它,子進程將無法打開它進行讀取。

我正在通過使用delete=False創建文件,在生成子進程之前關閉它,然后在完成后手動刪除它來解決這個問題:

fileTemp = tempfile.NamedTemporaryFile(delete = False)
try:
    fileTemp.write(someStuff)
    fileTemp.close()
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(fileTemp.name)

這似乎不優雅。 有一個更好的方法嗎? 也許是一種打開臨時文件權限的方法,以便子進程可以訪問它?

由於似乎沒有其他人有興趣公開這些信息......

tempfile確實公開了一個函數mkdtemp() ,它可以解決這個問題:

try:
    temp_dir = mkdtemp()
    temp_file = make_a_file_in_a_dir(temp_dir)
    do_your_subprocess_stuff(temp_file)
    remove_your_temp_file(temp_file)
finally:
    os.rmdir(temp_dir)

我將中間函數的實現留給讀者,因為人們可能希望使用mkstemp()來加強臨時文件本身的安全性,或者在刪除文件之前就地覆蓋文件。 我不特別知道通過仔細tempfile的源代碼可能不容易計划的安全限制。

無論如何,是的,在 Windows 上使用NamedTemporaryFile可能不優雅,我在這里的解決方案也可能不優雅,但是您已經確定 Windows 支持比優雅的代碼更重要,所以您不妨繼續做一些可讀的事情。

根據理查德 Oudkerk

(...) 在 Windows 上嘗試重新打開NamedTemporaryFile失敗的唯一原因是,當我們重新打開時,我們需要使用O_TEMPORARY

他給出了一個如何在 Python 3.3+ 中執行此操作的示例

import os, tempfile

DATA = b"hello bob"

def temp_opener(name, flag, mode=0o777):
    return os.open(name, flag | os.O_TEMPORARY, mode)

with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()
    with open(f.name, "rb", opener=temp_opener) as f:
        assert f.read() == DATA

assert not os.path.exists(f.name)

因為 Python 2.x 中內置的open()中沒有opener參數,所以我們必須結合較低級別os.open()os.fdopen()函數來達到相同的效果:

import subprocess
import tempfile

DATA = b"hello bob"

with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()

    subprocess_code = \
    """import os
       f = os.fdopen(os.open(r'{FILENAME}', os.O_RDWR | os.O_BINARY | os.O_TEMPORARY), 'rb')
       assert f.read() == b'{DATA}'
    """.replace('\n', ';').format(FILENAME=f.name, DATA=DATA)

    subprocess.check_output(['python', '-c', subprocess_code]) == DATA

你總是可以去低級,但不確定它是否對你來說足夠干凈:

fd, filename = tempfile.mkstemp()
try:
    os.write(fd, someStuff)
    os.close(fd)
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(filename)

至少如果您使用現有的 Python 庫打開一個臨時文件,則在 Windows 的情況下無法從多個進程訪問它。 根據MSDN ,您可以為CreateFile()函數指定第三個參數( dwSharedMode )共享模式標志FILE_SHARE_READ

啟用對文件或設備的后續打開操作以請求讀取訪問權限。 否則,如果其他進程請求讀取訪問權限,它們將無法打開文件或設備。 如果未指定此標志,但已打開文件或設備以進行讀取訪問,則函數失敗。

因此,您可以編寫一個 Windows 特定的 C 例程來創建一個自定義的臨時文件打開器函數,從 Python 調用它,然后您可以讓您的子進程訪問該文件而不會出現任何錯誤。 但我認為你應該堅持你現有的方法,因為它是最便攜的版本,可以在任何系統上工作,因此是最優雅的實現。

  • 關於 Linux 和 windows 文件鎖定的討論可以在這里找到。

編輯:原來也可以從 Windows 中的多個進程打開和讀取臨時文件。 請參閱 Piotr Dobrogost 的回答

with語句中使用mkstemp()代替os.fdopen()可以避免調用close()

fd, path = tempfile.mkstemp()
try:
    with os.fdopen(fd, 'wb') as fileTemp:
        fileTemp.write(someStuff)
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(path)

我知道這是一篇很老的帖子,但我認為它在今天是相關的,因為 API 正在發生變化,並且 mktemp 和 mkstemp 等函數正在被 TemporaryFile() 和 TemporaryDirectory() 等函數取代。 我只是想在以下示例中演示如何確保臨時目錄在下游仍然可用:

而不是編碼:

tmpdirname = tempfile.TemporaryDirectory()

並在整個代碼中使用 tmpdirname ,您應該嘗試在 with 語句塊中使用您的代碼,以確保它可用於您的代碼調用......像這樣:

with tempfile.TemporaryDirectory() as tmpdirname:
    [do dependent code nested so it's part of the with statement]

如果您在 with 之外引用它,那么它很可能不再可見。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM