我必须在这里犯一个愚蠢的错误,因为这个应该正在工作。我认为文件是开放的还是什么东西,它让我疯狂。
这适用于我所拥有的一些回归测试案例,我将比较模拟文件的脚本生成输出与已知的良好输出文件(密钥文件)进行比较。
这是一个简单的例子:
def run_and_compare(self, key_file, out_file, option):
print filecmp.cmp(out_file, key_file) # always True (as long as I've run this before, so the out_file exists already)
cmd = './analyze_files.py -f option'
with open(out_file, 'wb') as out:
subprocess.Popen(cmd.split(), stdout=out, stderr=subprocess.PIPE)
print filecmp.cmp(out_file, key_file) # always False
time.sleep(5)
print filecmp.cmp(out_file, key_file) # always True
我真的不想在测试中保持睡眠状态!如何在不使用睡眠的情况下确定输出文件是否正常?我尝试过使用out.close(),但它不起作用,只要我使用'就不需要它。我在这里使用python 2.6.4。
答案 0 :(得分:2)
将输出文件对象打开为上下文管理器并不重要。如果你明确地手动关闭了文件对象,那就无所谓了。
那是因为当您将Python文件对象传递给subprocess.Popen()
时,从该文件对象获取的只是文件句柄,这是操作系统用来与打开文件进行通信的整数。然后,子进程使用os.dup2()
将该文件句柄克隆到子进程的STDOUT文件句柄上;这是导致该子进程的输出转到磁盘上指定文件的原因。
因为文件句柄被欺骗,关闭原始Python文件对象(以及间接地,原始OS文件句柄)将不会实际关闭文件,因为第二个文件句柄仍然保持打开状态
您在等待几秒后看到文件数据出现的原因是因为最终您创建的子流程将完成,而只有才会关闭其他欺骗文件句柄。
不要等待几秒钟,而是等待子进程使用Popen.communicate()
method完成:
p = subprocess.Popen(cmd.split(), stdout=open(out_file, 'wb'),
stderr=subprocess.PIPE)
stdout, stderr = p.communicate() # stdout will always be None
我内联了open()
调用,因为subprocess.Popen()
从中检索文件句柄后,该文件对象没有其他用途。您也可以使用os.open()
而不是open()
(相同的参数),并自己创建一个只有文件句柄就足够的Python文件对象。
不要使用p.wait()
;因为您正在使用管道作为子进程的STDERR流,如果不从STDERR读取但是子进程写了很多数据到它。你最终会等待。
答案 1 :(得分:1)
我建议您在子流程中添加wait
以等待其完成
with open(out_file, 'wb') as out:
p=subprocess.Popen(cmd.split(), stdout=out, stderr=subprocess.PIPE)
p.wait()
如果您不等待,则子进程启动,将文件out
作为输出并立即返回(在后台启动)。
当你比较两个文件时,一个可能是空的,因此是假的。
过了一会儿,子进程结束,不再使用out
,可能是垃圾回收,处理已关闭:你的文件是有效的。
(我不是说这正是这里发生的事情,但缺少p.wait()
肯定是这里的问题)
除此之外,我一直想知道为什么人们运行涉及python命令的子进程,因为导入它们并直接调用它们的函数非常简单,从而受益于异常链,一个唯一的进程,避免了所有这些进程间通信问题。