在subprocess.Popen中关闭stdin

3 投票
2 回答
5481 浏览
提问于 2025-04-18 04:09

假设我写了一个函数。现在的问题是,另一个函数需要用到 voronoi.out 这个文件作为输入。我不知道的是,在 sp.Popen 完成后,怎么才能关闭这个 stdin。如果不关闭的话,qvoronoi 程序会一直“挂着”,直到我的脚本结束,这样下一个函数就无法读取 voronoi.out 文件了。

def voronoi(self):

    ''' This function calls qvoronoi program to create 
file with vertices.'''

    self.inp=open(self.out_name, 'r') #Redirect input and output for qvoronoi script
    self.out=open('voronoi.out', 'w') 

    sp.Popen(['./qvoronoi', 'p', 'FN'], stdin=self.inp, stdout=self.out) #Execute qvoronoi

    self.out.close() #Safely close output file

2 个回答

2

在子进程启动之前,有可能关闭它的标准输入(stdin):

from sys import stdin
import subprocess
subprocess.run(cmd, preexec_fn=stdin.close)

这样做并不会真正“关闭”标准输入,但可以确保标准输入是一个空文件,而且在Windows系统上也能正常工作:

subprocess.run(cmd, stdin=subprocess.DEVNULL)

这两种解决方案都避免了其他答案中提到的竞争条件。很多命令在启动时会检查标准输入,并根据检查结果改变它们的行为,所以如果在启动后才关闭标准输入,可能会导致一些不可预测的结果。

3

主要的问题是你没有等程序结束就退出了。qvoronio在你的函数结束时仍在使用文件,这样其他程序可能会被阻塞。而且因为你没有等待,qvoronio在执行完后会处于一种“僵尸”状态,看起来就像是卡住了一样。另外,你把临时的输入/输出文件句柄放在了你的对象上,但其实你应该马上关闭它们。

一些更热衷于Python的人喜欢使用上下文管理器来确保文件能正确关闭,可能会这样做:

def voronoi(self):
    with open(self.out_name, 'r') as inp:
        with open('voronoi.out', 'w') as outp: 
            qvoronio = sp.Popen(['./qvoronoi', 'p', 'FN'], stdin=inp, stdout=oup)
    qvoronio.wait()

注意,等待是在上下文管理器退出后进行的。你在把文件交给子进程后,在父进程中关闭了文件。

我通常会这样做:

def voronoi(self):
    qvoronio = sp.Popen(['./qvoronoi', 'p', 'FN'], stdin=open(self.out_name, 'r'),
        stdout=open('voronoi.out', 'w'))
    qvoronio.wait()

虽然这样做可能会遭到一些纯粹主义者的反对。我的方法在CPython中运行得很好,但在其他版本中可能会遇到问题。

撰写回答