为什么multiprocessing.Process无法重定向内部子进程的stdout/stderr?
原因说明
- Python 层面修改
sys.stdout/sys.stderr仅会影响 Python runtime 内的输出操作(比如print、sys.stdout.write),并不会修改进程在内核层面绑定的标准输出(fd1)、标准错误(fd2)文件描述符。 subprocess创建子进程时默认继承的是父进程内核级的文件描述符,和 Python 层的sys.stdout对象无关,所以你在multiprocessing子进程里修改sys.stdout完全不会影响内部subprocess.run的输出目标,这就是tasklist输出还是打印到控制台的原因。- 而你调用
subprocess.Popen时传入了stdout=fh参数,这个参数会直接修改新进程内核级的 fd1 指向打开的文件,后续该进程内部创建的所有子进程都会继承这个 fd,所以输出自然落到了文件里。
正确重定向multiprocessing.Process输出的方案
方案1:子进程内替换内核级文件描述符(全局生效)
在子进程逻辑最开头用os.dup2修改内核级的 fd 绑定,后续所有输出(包括 Python 自身输出和子进程调用的输出)都会指向指定目标:
import os import sys import subprocess def sub(): # 打开输出文件,buffering=0是无缓冲实时写入,可按需调整 with open("out.log", "w", buffering=0) as log_f: # 替换标准输出fd1 os.dup2(log_f.fileno(), sys.stdout.fileno()) # 替换标准错误fd2,这里合并到标准输出,也可以单独指定其他文件 os.dup2(log_f.fileno(), sys.stderr.fileno()) # 以下输出都会写入out.log subprocess.run(["tasklist"]) print("Test")
方案2:单独指定subprocess.run的输出目标(仅对当前调用生效)
如果只需要重定向特定subprocess调用的输出,直接给调用传stdout/stderr参数即可:
def sub(): # 仅重定向tasklist的输出到文件 with open("out.log", "w") as f: subprocess.run(["tasklist"], stdout=f, stderr=subprocess.STDOUT) # print的输出还是会走原来的控制台 print("Test")
方案3:主进程捕获子进程输出(适合需要主进程处理输出的场景)
如果需要主进程收集、处理子进程的所有输出,可以通过管道对接子进程的标准输出:
from multiprocessing import Process, set_start_method, Pipe import os import sys import subprocess def sub(write_end): # 把子进程的标准输出/错误绑定到管道写端 os.dup2(write_end.fileno(), sys.stdout.fileno()) os.dup2(write_end.fileno(), sys.stderr.fileno()) write_end.close() subprocess.run(["tasklist"]) print("Test") if __name__ == "__main__": set_start_method("spawn") read_end, write_end = Pipe(duplex=False) p = Process(target=sub, args=(write_end,)) p.start() # 主进程不需要握写端,关闭避免阻塞 write_end.close() # 读取子进程输出写入文件 with open("out.log", "wb") as f: while True: try: chunk = read_end.recv_bytes(1024) f.write(chunk) except EOFError: break p.join()
内容的提问来源于stack exchange,提问作者schussda
相关产品推荐
相关产品推荐

