Windows下subprocess的CREATE_NEW_CONSOLE与stdout重定向问题及双输出需求问询
示例代码
test.bat
@echo off for /L %%i in (40, -1, 10) do ( echo working %%i test1 ping localhost -n 2 > nul ) echo Done!
main.py
import subprocess startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.wShowWindow = 5 process1 = subprocess.Popen('test.bat', stdout=subprocess.PIPE, text=True, creationflags=subprocess.CREATE_NEW_CONSOLE) while process1.poll() is None: test1 = process1.stdout.readline() print(test1.rstrip(), flush=True)
问题与解答
问题1:当通过stdout=subprocess.PIPE重定向输出后,新建的控制台窗口无任何内容,此时设置creationflags=subprocess.CREATE_NEW_CONSOLE的意义是什么?
当stdout=subprocess.PIPE将子进程的标准输出重定向到父进程后,新建控制台窗口确实看不到输出,但这个标志仍有实际作用:
- 为子进程提供独立的控制台环境,避免子进程的控制台相关操作(如直接调用Windows控制台API)与父进程控制台产生冲突。
- 部分依赖完整控制台上下文的程序必须在独立控制台中运行,即使输出被重定向,该标志能保证子进程获得合法的控制台环境,避免运行异常。
问题2:是否存在方法,通过subprocess.Popen启动上述bat文件时,既能让输出在控制台窗口中可见,又能将stdout捕获到变量中(无需将输出保存到文本文件)?
可以实现,有两种实用方案:
修改bat脚本实现双输出
在bat的输出语句后添加指向Windows控制台设备con的重定向,让内容同时输出到控制台和标准输出,这样Python父进程就能通过stdout=subprocess.PIPE捕获输出:@echo off for /L %%i in (40, -1, 10) do ( echo working %%i test1 > con echo working %%i test1 ping localhost -n 2 > nul ) echo Done! > con echo Done!Python侧通过Windows API分流输出
利用ctypes调用Windows内核API,复制子进程的输出句柄,让内容同时流向新控制台和父进程管道,无需修改bat脚本:import subprocess import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) def create_dual_output_handles(): # 创建读管道 h_read, h_write = wintypes.HANDLE(), wintypes.HANDLE() kernel32.CreatePipe(ctypes.byref(h_read), ctypes.byref(h_write), None, 0) # 获取当前控制台的标准输出句柄 h_console_out = kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE # 复制写句柄,实现输出分流 h_dup_write = wintypes.HANDLE() kernel32.DuplicateHandle( kernel32.GetCurrentProcess(), h_write, kernel32.GetCurrentProcess(), ctypes.byref(h_dup_write), 0, True, 2 # DUPLICATE_SAME_ACCESS ) return h_read, h_dup_write, h_write h_read, h_dup_write, h_write = create_dual_output_handles() startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESTDHANDLES startupinfo.hStdOutput = h_dup_write startupinfo.hStdError = h_dup_write startupinfo.wShowWindow = subprocess.SW_SHOW process = subprocess.Popen( 'test.bat', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, creationflags=subprocess.CREATE_NEW_CONSOLE, startupinfo=startupinfo ) # 读取并处理捕获的输出 while True: line = process.stdout.readline() if not line: break print(f"捕获输出: {line.rstrip()}") process.wait() # 清理句柄 kernel32.CloseHandle(h_read) kernel32.CloseHandle(h_write) kernel32.CloseHandle(h_dup_write)
内容的提问来源于stack exchange,提问作者Rhys
相关产品推荐
相关产品推荐

