Python中如何通过subprocess.Popen传递变量并终止子进程循环?
解决Python子进程录屏的两个问题(Windows环境)
问题1:正确传递video_name到子进程
错误原因排查
用sys.argv[1]失败大概率是主进程传参格式错误,或子进程接收逻辑有误;用stdin则可能是子进程未正确读取输入流,或主进程写入后未关闭流导致子进程阻塞。
正确的命令行传参实现
主进程代码:
import subprocess import sys video_name = "my_recording.mp4" # 用sys.executable获取当前环境的Python解释器路径,避免环境冲突 proc = subprocess.Popen( [sys.executable, "ScreenRecorder.py", video_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True )
子进程ScreenRecorder.py接收参数:
import sys # 优先使用传入的参数,无参数时用默认值 if len(sys.argv) >= 2: video_name = sys.argv[1] else: video_name = "default_recording.mp4" # 后续录屏逻辑直接使用video_name变量
验证要点
- 主进程必须用列表形式传参,不要把命令拼成字符串(Windows下易因空格、特殊字符出问题)
- 确保
ScreenRecorder.py和主脚本在同一目录,或传入脚本的绝对路径 - 可在子进程开头打印参数确认:
print(f"Received video name: {sys.argv[1]}")
问题2:主进程发送信号让子进程正常收尾
Windows下Python的signal模块支持有限,无法直接用SIGINT类信号优雅终止,推荐用管道通信或文件标志位实现,让子进程主动检测终止信号,退出循环后执行收尾代码。
方案1:管道通信(推荐)
主进程代码:
import subprocess import sys import time video_name = "my_recording.mp4" # 创建双向管道,支持主进程向子进程发送指令 proc = subprocess.Popen( [sys.executable, "ScreenRecorder.py", video_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # 模拟主业务逻辑,15秒后发送终止指令 time.sleep(15) proc.stdin.write("stop\n") proc.stdin.flush() # 必须刷新缓冲区,否则子进程收不到指令 # 等待子进程执行完收尾代码,获取输出 stdout, stderr = proc.communicate() print("子进程输出:", stdout) if stderr: print("子进程错误:", stderr)
子进程ScreenRecorder.py代码:
import sys import cv2 import numpy as np from PIL import ImageGrab import select video_name = sys.argv[1] if len(sys.argv)>=2 else "default.mp4" # 初始化录屏参数 screen_size = (1920, 1080) fourcc = cv2.VideoWriter_fourcc(*"mp4v") out = cv2.VideoWriter(video_name, fourcc, 20.0, screen_size) running = True while running: # 捕获屏幕帧并写入视频 img = ImageGrab.grab(bbox=(0, 0, screen_size[0], screen_size[1])) frame = np.array(img) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) out.write(frame) # 非阻塞检测stdin的终止指令 ready, _, _ = select.select([sys.stdin], [], [], 0.01) if ready: line = sys.stdin.readline().strip() if line == "stop": running = False # 循环结束后执行收尾代码 out.release() cv2.destroyAllWindows() print(f"视频已保存为:{video_name}")
方案2:文件标志位(简单易实现)
主进程代码:
import subprocess import sys import time import os video_name = "my_recording.mp4" stop_flag_file = "stop_recording.flag" # 确保标志文件不存在 if os.path.exists(stop_flag_file): os.remove(stop_flag_file) proc = subprocess.Popen( [sys.executable, "ScreenRecorder.py", video_name, stop_flag_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # 15秒后创建标志文件,触发子进程终止 time.sleep(15) with open(stop_flag_file, "w") as f: f.write("1") # 等待子进程完成并清理标志文件 stdout, stderr = proc.communicate() if os.path.exists(stop_flag_file): os.remove(stop_flag_file) print(stdout)
子进程ScreenRecorder.py代码:
import sys import cv2 import numpy as np from PIL import ImageGrab import os video_name = sys.argv[1] if len(sys.argv)>=2 else "default.mp4" stop_flag_file = sys.argv[2] if len(sys.argv)>=3 else "stop_recording.flag" screen_size = (1920, 1080) fourcc = cv2.VideoWriter_fourcc(*"mp4v") out = cv2.VideoWriter(video_name, fourcc, 20.0, screen_size) running = True while running: img = ImageGrab.grab(bbox=(0, 0, screen_size[0], screen_size[1])) frame = np.array(img) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) out.write(frame) # 检测标志文件是否存在,存在则终止循环 if os.path.exists(stop_flag_file): running = False # 收尾代码 out.release() cv2.destroyAllWindows() print(f"视频已保存:{video_name}")
注意事项
- 管道方案中,若
select模块在Windows下出现问题,可替换为每隔10帧尝试读取一次stdin,避免阻塞 - 无论用哪种方案,子进程的循环都要定期检测终止信号,避免长时间阻塞导致收不到指令
- 主进程必须用
proc.communicate()等待子进程执行完收尾代码,不要直接强制杀死进程
内容的提问来源于stack exchange,提问作者L_N_M
相关产品推荐
相关产品推荐

