如何让Python脚本暂停执行直至内层subprocess完成?
问题场景
需要创建批处理文件run.bat以启动另一个批处理文件command.bat,要求Python脚本暂停执行,直到内层子进程完全运行完毕。
此前尝试的两种方法均失败:
- 通过父进程启动时创建文件、结束时删除的方式判断进程状态,但内层进程为独立线程,该方法无效。
- 尝试提取批处理进程ID,用
tasklist检查进程是否存在,但仅能获取父subprocess的PID,无法拿到内层进程的PID,因此失败。相关代码如下:
import os import subprocess from datetime import datetime def check_process_exists(pid_to_check): try: output = os.popen('tasklist').read() print(output) pids = [] lines = output.splitlines() for line in lines[1:]: parts = line.split() if len(parts) >= 2: pid = parts[1] pids.append(pid) process_ids = [item for item in pids if item.isdigit()] process_ids = [int(item) for item in process_ids] print("process",process_ids) for pid in process_ids: if pid_to_check == pid: print("process found") return True print("not found") return False except subprocess.CalledProcessError: return False def create_run_bat_file(self): """ Create run.bat file with additional commands to create and delete a text file. """ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = "{}_{}".format(self.job_name, timestamp) text_file_path = os.path.join(os.environ["Temp"], "{}.txt".format(filename)) # Define the run.bat file path run_bat_file = os.path.join(self.loc_path, "run.bat") # Create the run.bat file with open(run_bat_file, "w") as fid: fid.write('@echo off\n') fid.write('echo Process Holder > "{}"\n'.format(text_file_path)) fid.write('start /B CMD /C "Q:/..../CPAU.exe %~dp0\\command.bat"\n') fid.write('ping 127.0.0.1 -n 2 > nul\n') fid.write('del "{}"\n'.format(text_file_path)) fid.write('pause\n') return run_bat_file, text_file_path def create_command_bat(self): # Create a common command.bat file cmd_bat_file = "%s/command.bat" % self.loc_path with open(cmd_bat_file, "w") as fid: line = "@echo off\n" line += 'robocopy "{}" "{}" '.format(self.src_path, self.dst_path) if self.op_type == "move_dir": line += '/MOVE /S /E' elif self.op_type == "copy_dir": line += '/S /E' elif self.op_type == "copy_file": line += self.file_name else: # Handle unsupported operation types here pass fid.write(line) def execute(self): self.create_user_copy_dir() update_log_file("\n created user_copy: %s" % self.loc_path) # Create the single command.bat file self.create_command_bat() #cmd_bat_file = run_bat_file, log_file_path = self.create_run_bat_file() # cpauprocess_pid = self.get_cpauprocess_pid() process = subprocess.Popen(run_bat_file,stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) # 原代码此处pid未定义,为遗漏问题 process_not_done = check_process_exists(pid) while process_not_done: process_not_done = check_process_exists(pid) print("Process is finished")
可行解决方案
方案1:修改run.bat,让父进程等待内层进程完成
问题核心在于run.bat中使用了start /B CMD /C,该命令会启动独立进程,父批处理不会等待其结束。可以通过以下两种方式修改:
方式A:移除start命令,直接执行
修改create_run_bat_file方法中的批处理内容,让run.bat直接等待CPAU和command.bat执行完毕:
def create_run_bat_file(self): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = "{}_{}".format(self.job_name, timestamp) text_file_path = os.path.join(os.environ["Temp"], "{}.txt".format(filename)) run_bat_file = os.path.join(self.loc_path, "run.bat") with open(run_bat_file, "w") as fid: fid.write('@echo off\n') # 直接执行CPAU,run.bat会等待其完成 fid.write('Q:/..../CPAU.exe "%~dp0\\command.bat"\n') fid.write('pause\n') return run_bat_file, text_file_path
同时修改execute方法,直接用communicate()等待父进程结束:
def execute(self): self.create_user_copy_dir() update_log_file("\n created user_copy: %s" % self.loc_path) self.create_command_bat() run_bat_file, log_file_path = self.create_run_bat_file() process = subprocess.Popen(run_bat_file, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) # 等待父进程结束,此时内层进程已完成 stdout, stderr = process.communicate() print("Process is finished")
方式B:使用start /WAIT强制等待
若必须保留start命令,可添加/WAIT参数让父进程等待:
start /WAIT /B CMD /C "Q:/..../CPAU.exe %~dp0\\command.bat"
方案2:直接跳过run.bat,Python直接调用CPAU
无需创建中间批处理文件,直接在Python中调用CPAU执行command.bat,可直接控制等待逻辑:
def execute(self): self.create_user_copy_dir() update_log_file("\n created user_copy: %s" % self.loc_path) cmd_bat_path = os.path.join(self.loc_path, "command.bat") self.create_command_bat() # 直接调用CPAU执行command.bat,等待完成 process = subprocess.Popen( ["Q:/..../CPAU.exe", cmd_bat_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) stdout, stderr = process.communicate() print("Process is finished")
方案3:获取内层进程PID(仅在必须用独立进程时使用)
若因特殊需求必须启动独立进程,可修改run.bat输出内层进程PID,Python读取后轮询等待:
修改run.bat内容:
@echo off setlocal enabledelayedexpansion :: 启动内层进程 start /B CMD /C "Q:/..../CPAU.exe %~dp0\\command.bat" :: 获取刚启动的CMD进程PID for /f "tokens=2" %%a in ('tasklist /fi "imagename eq cmd.exe" /fo list ^| findstr PID') do set inner_pid=%%a :: 将PID写入临时文件 echo !inner_pid! > "%temp%\inner_pid.txt" :: 轮询等待进程结束 :wait tasklist /fi "pid eq !inner_pid!" | findstr /i "cmd.exe" >nul if not errorlevel 1 ( timeout /t 1 >nul goto wait ) del "%temp%\inner_pid.txt" pause
Python中读取临时文件的PID后进行轮询(此方法可靠性低于前两种)。
内容的提问来源于stack exchange,提问作者Kartikey
相关产品推荐
相关产品推荐

