Python脚本执行完毕后无法退出Windows命令提示符求助排查
脚本执行后无法回到命令行的问题排查与修复
问题描述
编写的Python脚本功能为读取prompts.txt首行,执行一系列鼠标键盘操作后删除该行,循环最多执行10次。脚本看似运行完毕,但始终无法回到Windows命令提示符,疑似卡顿或循环异常,在cmd中运行问题依旧。原代码如下:
from pyautogui import moveTo, click, typewrite, hotkey import time import random import pyperclip import tkinter as tk from tkinter import messagebox from threading import Thread import fileinput # Open the text file and read all lines with open('prompts.txt', 'r') as file: lines = file.readlines() # Limit the number of lines to process num_lines = min(len(lines), 10) # Create a Tkinter window window = tk.Tk() window.title("Script Progress") window.geometry("200x150") # Set the window size # Create a label to display the countdown countdown_label = tk.Label(window, text="Countdown: {}".format(num_lines)) countdown_label.pack() # Function to stop the script def stop_script(): if messagebox.askokcancel("Stop Script", "Are you sure you want to stop the script?"): window.destroy() # Create a stop button stop_button = tk.Button(window, text="Stop", command=stop_script, width=10, height=3) # Set the button size stop_button.pack() # Function to update the countdown label def update_countdown_label(count): countdown_label.config(text="Countdown: {}".format(count)) # Function to perform the script logic def run_script(): # Loop until the counter reaches the specified number of lines or window is closed for i in range(num_lines): # Check if window is closed if not window.winfo_exists(): break # Left click inside the text field click(405, 995, 1, 1, 'left') # Generate random sleep duration between 1 and 10 seconds sleep_duration = random.uniform(3, 8) # Wait for the message to be sent time.sleep(sleep_duration) # Type / to get prompt typewrite('/', interval=0.2) # Generate random sleep duration between 1 and 10 seconds sleep_duration = random.uniform(5, 10) # Wait for the message to be sent time.sleep(sleep_duration) # Press enter typewrite(['enter']) # Generate random sleep duration between 1 and 10 seconds sleep_duration = random.uniform(5, 20) # Wait for the message to be sent time.sleep(sleep_duration) # Extract the first line and remove it from the list first_line = lines.pop(0).strip() # Remove the first line from the file with fileinput.input('prompts.txt', inplace=True) as file: for line in file: if line.strip() != first_line: print(line, end='') # Copy the first line to the clipboard pyperclip.copy(first_line) # Paste text from clipboard hotkey('ctrl', 'v') # Generate random sleep duration between 1 and 10 seconds sleep_duration = random.uniform(5, 20) # Wait for the message to be sent time.sleep(sleep_duration) # Press enter typewrite(['enter']) # Update the countdown label update_countdown_label(num_lines - i - 1) # Close the window when the script is finished window.destroy() # Run the script logic in a separate thread script_thread = Thread(target=run_script) script_thread.start() # Set window attributes for focus and stay on top window.lift() window.attributes('-topmost', True) # Start the Tkinter event loop window.mainloop()
注:原脚本缺少fileinput模块导入,已补充。
问题根源
- 非主线程操作Tkinter组件:
update_countdown_label在脚本线程中直接修改UI元素,违反了Tkinter必须在主线程处理UI操作的规则,会导致事件循环紊乱,引发卡顿或死锁。 - 文件删除逻辑缺陷:使用
fileinput的inplace=True时,通过line.strip() != first_line判断会删除所有内容匹配的行,而非仅文件的首行,若文件存在重复行会破坏数据,同时频繁IO操作可能引发阻塞。 - 线程收尾不彻底:脚本线程结束后销毁窗口,但主线程的
mainloop可能因异常状态无法正常退出,导致进程残留,无法回到命令提示符。
修复后的代码
from pyautogui import click, typewrite, hotkey import time import random import pyperclip import tkinter as tk from tkinter import messagebox from threading import Thread # Open the text file and read all lines with open('prompts.txt', 'r') as file: lines = [line.rstrip('\n') for line in file] # Limit the number of lines to process num_lines = min(len(lines), 10) # Create a Tkinter window window = tk.Tk() window.title("Script Progress") window.geometry("200x150") # Create a label to display the countdown countdown_label = tk.Label(window, text=f"Countdown: {num_lines}") countdown_label.pack(pady=20) # Function to stop the script def stop_script(): if messagebox.askokcancel("Stop Script", "Are you sure you want to stop the script?"): window.destroy() # Create a stop button stop_button = tk.Button(window, text="Stop", command=stop_script, width=10, height=2) stop_button.pack() # Function to update the countdown label (must run in main thread) def update_countdown_label(count): def safe_update(): if window.winfo_exists(): countdown_label.config(text=f"Countdown: {count}") # Use after to schedule UI update in main thread window.after(0, safe_update) # Function to remove first line from file correctly def remove_first_line(): with open('prompts.txt', 'r') as f: all_lines = f.readlines() if len(all_lines) > 0: with open('prompts.txt', 'w') as f: f.writelines(all_lines[1:]) # Function to perform the script logic def run_script(): try: for i in range(num_lines): if not window.winfo_exists(): break # Left click inside the text field click(405, 995, 1, 1, 'left') time.sleep(random.uniform(3, 8)) # Type / to get prompt typewrite('/', interval=0.2) time.sleep(random.uniform(5, 10)) # Press enter typewrite(['enter']) time.sleep(random.uniform(5, 20)) # Extract the first line and remove it from the list if not lines: break first_line = lines.pop(0) # Remove first line from file remove_first_line() # Copy and paste text pyperclip.copy(first_line) hotkey('ctrl', 'v') time.sleep(random.uniform(5, 20)) # Press enter typewrite(['enter']) # Update countdown in safe way update_countdown_label(num_lines - i - 1) finally: # Ensure window is closed even if exception occurs if window.winfo_exists(): window.after(0, window.destroy) # Set thread as daemon so it exits when main thread exits script_thread = Thread(target=run_script, daemon=True) script_thread.start() # Set window attributes window.lift() window.attributes('-topmost', True) # Start the Tkinter event loop window.mainloop()
修复说明
- UI操作线程安全:通过
window.after将UI更新调度到主线程执行,避免跨线程操作Tkinter组件的问题。 - 文件删除逻辑修正:重写文件时直接跳过首行,确保只删除文件的第一行,避免重复行误删。
- 线程守护与异常处理:将脚本线程设为守护线程,确保主线程退出时子线程也随之结束;添加
try-finally块保证窗口能正常销毁,避免进程残留。
内容的提问来源于stack exchange,提问作者CHLOE HARLOW
相关产品推荐
相关产品推荐

