Python 3:subprocess.run('mv')使目标保持打开状态

时间:2019-04-03 13:43:01

标签: python python-3.x

我有一个脚本,该脚本使用非常简单的基于IPC的文件与另一个程序进行通信。我将包含新内容的tmp文件写入mv到IPC文件中,以使内容保持原子状态(其他程序侦听重命名事件)。

但是现在有个问题:这工作了2到3次,但是交换被卡住了。

time.sleep(10)
# check lsof => target file not opened
subprocess.run(
    "mv /tmp/tempfile /tmp/target",
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    universal_newlines=True,
    shell=True,
)
# check lsof => target file STILL open
time.sleep(10)

/tmp/tempfile将为每次写入做好准备

第一次运行结果为:

$ lsof /tmp/target
COMMAND  PID        USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
python  1714      <user>   3u   REG   0,18      302 10058 /tmp/target

将其打开,直到我终止主python程序。连续运行会按预期方式更改内容,索引节点和文件描述符,但仍会打开mv所不希望的内容。

具有以上这些行的python程序被关闭时,文件最终被关闭。

编辑:

发现了错误:tempfile.mkstemp()处理不当。参见:https://docs.python.org/3/library/tempfile.html#tempfile.mkstemp

我这样创建了临时文件:

_fd, temp_file_path = tempfile.mkstemp()

我丢弃了默认情况下打开的文件描述符_fd 。我没有关闭它,因此即使在移动后它也保持打开状态。这导致了一个打开的目标,并且由于我只是lsof在该目标上,所以我没有看到临时文件已经打开。这将是正确的版本:

fd, temp_file_path = tempfile.mkstemp()
fd.write(content)
fd.close()

# ... mv/rename via shell execution/shutil/pathlib

非常感谢大家的帮助和建议!

3 个答案:

答案 0 :(得分:1)

有什么理由不使用shutil.move?否则,可能需要等待mv命令完成移动然后杀死它,阅读stdin并运行

之类的命令
p = subprocess.run(...)
# wait to finish moving/read from stdin
p.terminate()

当然terminate有点苛刻。

编辑:根据您的使用情况rsync(不是python的一部分)可能是一种很好的解决方案,可以使您的数据在网络上保持同步,而无需编写任何代码

答案 1 :(得分:1)

我无法重现此行为。我创建了一个文件/tmp/tempfile,并通过您发出的subprocess.run调用运行了python脚本,然后进行了长时间的睡眠。 /tmp/target未被使用,在lsof -p <pid>中也没有看到任何意外打开的文件。

(edit)我对此并不感到惊讶,因为您的子进程命令无法打开文件:mv不会打开其参数(您可以使用ltrace进行检查),并且subprocess.run不会解析其参数或对其进行任何处理,除了将其传递给exec-ed。

但是,当我添加一些行来打开文件并写入该文件然后移动该文件时,我看到的是与您描述的相同的行为。这是代码:

import subprocess
out=open('/tmp/tempfile', 'w')
out.write('hello')
subprocess.run(
    "mv /tmp/tempfile /tmp/target",
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    universal_newlines=True,
    shell=True,
)
import time
time.sleep(5000)

在这种情况下,该文件仍处于打开状态,因为它从未关闭过,即使已被重命名,原始文件句柄仍然存在。我敢打赌,您的代码中有一些类似的东西正在创建此文件,并为其打开一个句柄。

答案 2 :(得分:0)

您说它仍然被“ mv”打开,但是lsof结果显示为python打开。由于它是子进程,因此请查看pid是否与python进程相同,也许是另一个python进程。