代码之家  ›  专栏  ›  技术社区  ›  Nathan Reed

如何创建一个子流程可以读取的临时文件?

  •  63
  • Nathan Reed  · 技术社区  · 12 年前

    我正在编写一个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)
    

    这看起来很不雅。有更好的方法吗?也许是一种打开临时文件权限的方法,以便子流程可以访问它?

    6 回复  |  直到 12 年前
        1
  •  26
  •   Corbin    12 年前

    由于似乎没有其他人对公开这些信息感兴趣。。。

    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() 以加强临时文件本身的安全性,或者在删除文件之前覆盖文件。我不特别知道通过仔细阅读 临时文件 .

    无论如何,是的,使用 NamedTemporaryFile 在Windows上可能不雅,我在这里的解决方案可能也不雅,但您已经决定Windows支持比优雅的代码更重要,所以您不妨继续做一些可读的事情。

        2
  •  25
  •   Piotr Dobrogost    12 年前

    According 致Richard Oudkerk

    (…)试图重新打开 NamedTemporaryFile 在上失败 Windows是因为当我们重新打开时,我们需要使用 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)
    

    因为没有 opener 内置中的参数 open() 在Python2.x中,我们必须结合较低级别 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
    
        3
  •  13
  •   tshepang Arrie    12 年前

    你可以一直保持低水平,但不确定它是否足够干净:

    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)
    
        4
  •  11
  •   Community CDub    7 年前

    至少如果您使用现有的Python库打开一个临时文件,那么在Windows的情况下,从多个进程访问它是不可能的。根据 MSDN 可以指定第三个参数( dwSharedMode )共享模式标志 FILE_SHARE_READ CreateFile() 功能:

    启用对文件或设备的后续打开操作以请求读取 通道否则,其他进程无法打开文件或设备,如果 他们请求读取访问。如果未指定此标志,但文件 或设备已打开进行读取访问,则功能失败。

    因此,您可以编写一个特定于Windows的C例程来创建一个自定义的临时文件打开程序函数,从Python中调用它,然后您可以让您的子进程访问该文件而不会出现任何错误。但我认为您应该坚持现有的方法,因为它是最可移植的版本,可以在任何系统上工作,因此是最优雅的实现。

    • 可以找到关于Linux和windows文件锁定的讨论 here .

    编辑:事实证明,打开&也从Windows中的多个进程中读取临时文件。参见Piotr Dobrogost的 answer .

        5
  •  0
  •   Jon-Eric    3 年前

    使用 mkstemp() 取而代之的是 os.fdopen() 在一个 with 语句避免了必须调用 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)
    
        6
  •  0
  •   Clint Box    2 年前

    我知道这是一篇非常古老的帖子,但我认为它与今天的内容相关,因为API正在改变,mktemp和mkstemp等函数正在被TemporaryFile()和TemporaryDirectory()等函数所取代。我只是想在下面的示例中演示如何确保temp目录在下游仍然可用:

    不用编码:

    tmpdirname = tempfile.TemporaryDirectory()
    

    并且在整个代码中使用tmpdirname,您应该尝试在with语句块中使用您的代码,以确保它可用于您的代码调用。。。这样地:

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

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