在Python中分叉多个shell命令/进程的最佳方法是什么?

4 投票
5 回答
5291 浏览
提问于 2025-04-17 06:06

我看到的大多数关于 os.fork 和 subprocess/multiprocessing 模块的例子,都是在讲如何复制一个新的 Python 脚本实例或者一段 Python 代码。那有没有什么好的方法可以同时运行一组任意的 shell 命令呢?

我想,我可以直接使用 subprocess.call 或者某个 Popen 命令,然后把输出结果写入一个文件。我觉得这样做会很快返回结果,至少对调用者来说是这样的。我知道这并不难,我只是想找出最简单、最符合 Python 风格的方法来实现。

提前谢谢你们!

5 个回答

1

我喜欢用伪终端(PTYs)来代替管道。对于一些我只想捕捉错误信息的进程,我是这么做的。

RNULL = open('/dev/null', 'r')
WNULL = open('/dev/null', 'w')
logfile = open("myprocess.log", "a", 1)
REALSTDERR = sys.stderr
sys.stderr = logfile

接下来的部分是在一个循环中启动大约30个进程。

sys.stderr = REALSTDERR
master, slave = pty.openpty()
self.subp = Popen(self.parsed, shell=False, stdin=RNULL, stdout=WNULL, stderr=slave)
sys.stderr = logfile

之后我有一个select循环,用来收集任何错误信息,并把它们发送到一个单独的日志文件。使用伪终端的好处是,我不用担心行内容会混在一起,因为行的管理方式让每一行都很清晰。

1

我想,我可以直接用subprocess.call或者Popen命令,把输出结果导入到一个文件里,这样应该会立刻返回结果,至少对调用者来说是这样的。

不过,如果你想处理这些数据,这样做就不太合适了。

在这种情况下,最好是这样做:

sp = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)

然后可以用sp.communicate()或者直接从sp.stdout.read()读取数据。

如果你打算在后面某个时间处理这些数据,有两种方法可以选择:

  1. 你可以尽快获取数据,可能通过一个单独的线程来读取数据,并把它存储在某个地方,方便后续使用。

  2. 你也可以让生成数据的子进程阻塞,等到你需要数据时再从它那里获取。这个子进程会尽量把数据写入管道缓冲区(通常是64KB),然后在写入更多数据时会阻塞。只要你需要数据,就可以从子进程的stdout(也可能是stderr)中read(),然后使用这些数据——或者在稍后的时间再次使用sp.communicate()

如果生成数据需要很长时间,第一种方法比较合适,这样你的程序就不需要一直等待。

如果数据量很大,或者数据生成得非常快,以至于缓冲没有意义,那么第二种方法更为推荐。

4

所有对 subprocess.Popen 的调用都会立即返回给调用者,也就是说,当你启动一个新进程时,程序不会停下来等这个进程完成。而是要等你调用 waitcommunicate 时,程序才会暂停,等这些进程完成。所以,你只需要用 subprocess.Popen 启动多个进程(为了安全,输入可以设置为 /dev/null),然后一个一个地调用 communicate,直到所有进程都完成。

当然,我假设你只是想启动一堆没有关系的(也就是不相互连接的)命令。

撰写回答