Windows下Python subprocess不遵循stdout追加模式问题及解决方案咨询
问题分析
这不是Python的bug,是Windows与类Unix系统底层文件模型的固有差异导致的现象:
- Linux系统中
O_APPEND是文件描述符关联的内核级属性,子进程继承文件描述符后属性依然生效,所有写入操作都会自动定位到文件末尾,不会出现覆盖或空洞。 - Windows系统中C runtime的
O_APPEND标记是进程内的用户态属性,不会随文件句柄继承传递给子进程。子进程拿到继承的文件句柄后,写入操作会使用自身维护的文件指针偏移,不会自动追加到文件末尾。 - 你复现代码中父进程调用
file.write_text('')会将文件截断为0长度,但子进程的文件指针还停留在原文件长度(11字节)的位置,子进程写入时就会在前面填充11个NUL字符,最终出现你看到的异常输出。
解决方案
方案1:子进程输出父进程追加(最稳妥,跨平台兼容)
不要在父进程打开文件传递给子进程,改为父进程捕获子进程的标准输出,再手动追加到目标文件,无需修改子进程代码也不需要区分操作系统:
import subprocess, time, pathlib, sys print(f'Caller {sys.platform=} {sys.version=}') pathlib.Path('sub.py').write_text("""import sys, time time.sleep(1) print(f'Callee {sys.stdout.buffer.mode=}')""") file = pathlib.Path('dummy.txt') file.write_text('Hello World') # 捕获子进程输出 popen = subprocess.Popen([sys.executable, 'sub.py'], stdout=subprocess.PIPE, text=True) stdout, _ = popen.communicate() # 父进程手动追加到文件 with open(file, 'a') as f: f.write(stdout) print(file.read_text())
方案2:Windows专属传递可继承追加句柄
如果必须在父进程打开文件传递给子进程,Windows下需要手动创建带FILE_APPEND_DATA权限的可继承句柄:
import subprocess, time, pathlib, sys import win32file, win32con, msvcrt print(f'Caller {sys.platform=} {sys.version=}') pathlib.Path('sub.py').write_text("""import sys, time time.sleep(1) print(f'Callee {sys.stdout.buffer.mode=}')""") file = pathlib.Path('dummy.txt') file.write_text('Hello World') # 创建支持追加的可继承Windows文件句柄 handle = win32file.CreateFileW( str(file), win32con.FILE_APPEND_DATA, win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE, None, win32con.OPEN_ALWAYS, win32con.FILE_ATTRIBUTE_NORMAL, None ) # 转换为C runtime文件描述符 fd = msvcrt.open_osfhandle(handle, 0) f = open(fd, 'a') # 传递句柄时必须设置close_fds=False popen = subprocess.Popen([sys.executable, 'sub.py'], stdout=f, close_fds=False) file.write_text('') time.sleep(2) print(file.read_text())
内容的提问来源于stack exchange,提问作者Wolfgang Kuehn
相关产品推荐
相关产品推荐

