简介:这是一份面向IT运维人员与Python学习者的Windows远程批量关机脚本资源,针对多台设备统一关机、故障排查与日常维护场景,提供可直接参考的自动化实现思路。压缩包共3个文件,包含1个py脚本与2个txt说明文档,整体仅923B,体积轻巧便于快速取用;脚本以win32com.client调用WMI服务连接远程主机,通过Win32_ComputerSystem的Shutdown方法执行软关机或强制关机,并支持遍历IP列表完成批量操作,说明文档则补充权限配置与使用要点。资源已有897人学习下载,适合需要批量管理终端、希望掌握WMI远程调用与异常处理技巧的读者,可据此快速搭建脚本框架,理解用户名密码鉴权、目标机远程管理策略设置及重试超时等健壮性优化方向,提升多设备运维效率。
1. 批量远程关机这件事,Python 在 Windows 下到底能省多少事
机房巡检完最后一批机器,凌晨两点半,你手里攥着一张 Excel 表,上面列着三十多台 Windows 主机的 IP、账号、密码。一台一台远程桌面连过去点关机,点完最后一台天都亮了。这种场景在实验室、测试机房、临时搭建的渲染农场里太常见了。Python 在 Windows 下做自动远程批量关机,核心思路就两条路:一条走 WMI(Windows Management Instrumentation),一条走系统自带的 shutdown 命令配合凭据。两条路都不需要装额外服务端,只要目标机器开了对应的端口和权限就能跑。适合谁?手头有十几台以上 Windows 机器需要统一关停、又不想逐台操作的运维或开发。下面把我自己踩过的坑和能直接抄的代码拆开讲。
2. 两条技术路线选型:WMI 与 shutdown 命令的取舍
2.1 WMI 方案:适合需要精细控制的场景
WMI 是 Windows 原生的管理接口,Python 通过pywin32库调用它。好处是能拿到目标机器的详细状态,比如当前登录用户、运行中的进程,甚至可以在关机前做一次条件判断。坏处是依赖 DCOM 配置,跨网段或者目标机器防火墙策略严格时,连接容易被拒。
我一般会先用 WMI 做一次探测,确认目标机器可达且凭据有效,再决定是否执行关机。探测代码如下:
import wmi def check_host(ip, username, password): """ 通过 WMI 连接目标主机,返回系统信息或 None ip: 目标主机 IP username: 管理员账号 password: 对应密码 """ try: # 指定目标 IP、账号、密码建立 WMI 连接 conn = wmi.WMI( computer=ip, user=username, password=password, namespace="root\\cimv2" # 默认命名空间,查系统信息够用 ) # 拿操作系统信息作为连通性验证 os_info = conn.Win32_OperatingSystem()[0] return os_info.Caption # 例如 "Microsoft Windows 10 专业版" except Exception as e: print(f"[FAIL] {ip} 连接失败: {e}") return None这段代码的逻辑很直白:用wmi.WMI()建立连接,namespace参数指定root\cimv2,这是查大多数硬件和系统信息的标准入口。Win32_OperatingSystem()返回一个列表,取第一个元素的Caption属性就是系统版本。如果连接失败会抛异常,捕获后打印失败原因。参数方面,computer填 IP 或主机名都行,user建议用域名\账号格式,比如WORKGROUP\Administrator,不写域名有时会认证失败。
2.2 shutdown 命令方案:轻量、快、但控制粒度粗
Windows 自带的shutdown.exe支持远程关机,语法是shutdown /s /m \\目标IP /t 0 /f。Python 里用subprocess调它就行。这个方案不依赖 DCOM,走的是 RPC,目标机器只要开了“文件和打印机共享”以及远程注册表服务就能通。缺点是没法在关机前做复杂判断,而且凭据传递要靠net use先建立 IPC 连接。
import subprocess def shutdown_via_cmd(ip, username, password): """ 先用 net use 建立 IPC 连接,再调用 shutdown 命令 """ # 建立 IPC$ 连接,为后续 shutdown 提供凭据 subprocess.run( f'net use \\\\{ip}\\IPC$ /user:{username} {password}', shell=True, capture_output=True, text=True ) # 执行远程关机,/t 0 表示立即,/f 强制关闭应用 result = subprocess.run( f'shutdown /s /m \\\\{ip} /t 0 /f', shell=True, capture_output=True, text=True ) # 用完删除连接,避免凭据残留 subprocess.run(f'net use \\\\{ip}\\IPC$ /delete /y', shell=True) return result.returncodenet use那一步是关键,没有它,shutdown /m会报“拒绝访问”。/user:后面跟账号,密码直接跟在后面。capture_output=True把输出抓回来,方便判断是否成功。/t 0是倒计时零秒,/f强制结束未保存的应用。最后一定要net use /delete,否则多次执行会累积连接,导致“多重连接”错误。
2.3 怎么选:看目标机器数量和网络环境
如果目标机器在同一个域里,WMI 方案更稳,因为域账号认证走 Kerberos,不需要明文传密码。如果是工作组环境,机器数量在 20 台以内,shutdown 命令方案更快落地,代码量也少。超过 50 台,建议用 WMI 加线程池并发,但要注意 DCOM 的并发连接数限制,一般开到 10 到 15 个线程就差不多了,再高容易触发 RPC 超时。
3. 批量执行框架:从单台到多台的工程化改造
3.1 读取主机清单与凭据管理
硬编码 IP 和密码是大忌。我一般用 CSV 存主机列表,密码单独放一个文件或者用环境变量。CSV 格式如下:
ip,username,password 192.168.1.101,Administrator,Pass123! 192.168.1.102,Administrator,Pass123!读取代码:
import csv def load_hosts(csv_path): """ 从 CSV 加载主机列表,返回字典列表 """ hosts = [] with open(csv_path, newline='', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: hosts.append({ 'ip': row['ip'].strip(), 'username': row['username'].strip(), 'password': row['password'].strip() }) return hostsDictReader自动把表头映射成键,strip()去掉可能存在的空格。密码字段如果不想明文存,可以改成读环境变量,比如os.environ.get(f"PASS_{row['ip']}"),这样 CSV 里只留 IP 和账号。
3.2 并发控制与超时设置
串行关机太慢,30 台机器每台等 5 秒就是两分半。用concurrent.futures.ThreadPoolExecutor并发,但必须设超时,否则一台卡死的机器会拖垮整个任务。
from concurrent.futures import ThreadPoolExecutor, as_completed import time def batch_shutdown(hosts, max_workers=10, timeout=15): """ 并发批量关机 hosts: 主机字典列表 max_workers: 并发线程数,建议 10-15 timeout: 单台超时秒数 """ results = {} with ThreadPoolExecutor(max_workers=max_workers) as executor: # 提交任务,future 与 ip 建立映射 future_map = { executor.submit(shutdown_via_cmd, h['ip'], h['username'], h['password']): h['ip'] for h in hosts } for future in as_completed(future_map, timeout=timeout * len(hosts)): ip = future_map[future] try: code = future.result(timeout=timeout) results[ip] = '成功' if code == 0 else f'返回码 {code}' except Exception as e: results[ip] = f'异常: {e}' return resultsmax_workers控制同时关机的机器数,设太大目标网络会拥塞,设太小又失去并发意义。as_completed的timeout参数是总超时,我一般设成单台超时 × 机器数,防止某台机器无响应导致主线程永久阻塞。future.result(timeout=timeout)是单台超时,超过就抛TimeoutError,记录异常继续处理下一台。
3.3 执行结果记录与重试机制
关机这种操作,失败一次就得人工补,所以结果必须落盘。我习惯写一个简单的日志文件,格式是时间戳,IP,状态。重试逻辑只对“连接超时”和“返回码 5(拒绝访问)”以外的错误重试一次,因为拒绝访问重试也没用,得先修权限。
import logging logging.basicConfig( filename='shutdown_log.txt', level=logging.INFO, format='%(asctime)s,%(message)s' ) def shutdown_with_retry(ip, username, password, retries=1): """ 带一次重试的关机封装 """ for attempt in range(retries + 1): code = shutdown_via_cmd(ip, username, password) if code == 0: logging.info(f"{ip},成功") return True elif code == 5: logging.error(f"{ip},拒绝访问,不重试") return False else: logging.warning(f"{ip},第{attempt+1}次失败,返回码{code}") time.sleep(2) # 等两秒再试 logging.error(f"{ip},重试后仍失败") return Falselogging模块直接输出 CSV 格式的日志,方便后续用 Excel 打开筛选。retries=1表示最多试两次。time.sleep(2)给目标机器一点缓冲时间,尤其是刚开机不久的系统,服务还没完全起来。
4. 避坑指南:远程关机最常见的五个翻车点
4.1 现象:报“拒绝访问”错误码 5
原因:目标机器的账号不是管理员组,或者 UAC 远程限制没关。工作组环境下,即使账号是 Administrator,如果目标机器开了 UAC 远程限制,也会拒绝。
解决:在目标机器上把LocalAccountTokenFilterPolicy注册表项设为 1。命令是reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f。这个操作需要重启目标机器或者重启相关服务才生效。
4.2 现象:WMI 连接超时,但 ping 得通
原因:目标机器防火墙拦了 DCOM 的端口。WMI 走的是 TCP 135 和动态端口范围,很多安全策略只开了 135,动态端口没放行。
解决:在目标机器防火墙里放行“Windows Management Instrumentation (WMI)”入站规则。如果没法改防火墙,换 shutdown 命令方案,它走 RPC over SMB,通常 445 端口是通的。
4.3 现象:shutdown 命令返回“系统找不到指定的文件”
原因:net use那一步没成功建立 IPC 连接,或者密码里有特殊字符被 shell 转义了。
解决:检查net use的返回输出,确认连接建立。密码里如果有&、|、>这类字符,用双引号把密码包起来,或者改用subprocess.run的列表参数形式,避免 shell 解析。
4.4 现象:并发执行时部分机器报“多重连接”
原因:同一个目标 IP 被多个线程同时net use,Windows 不允许同一目标有多重凭据连接。
解决:在shutdown_via_cmd里加一个简单的文件锁,或者把并发数降到 5 以下。更彻底的办法是改用 WMI 方案,WMI 不依赖 IPC 连接,没这个问题。
4.5 现象:关机命令发出去了,但机器没关
原因:目标机器上有未保存的文档,/f参数虽然强制关闭应用,但某些系统进程会阻止关机。或者目标机器设置了“关机需要等待用户响应”。
解决:在关机前先用 WMI 查一下Win32_Process里有没有notepad、excel这类进程,有的话先发消息提醒。或者改用shutdown /s /m \\IP /t 30 /c "系统将在30秒后关闭",给用户一个保存的机会。
5. 进阶技巧:用 WMI 事件订阅做关机前检查
前面讲的都是“直接关”,但有些场景下你希望目标机器在关机前先执行一段清理脚本,比如删除临时文件、上传日志。WMI 支持事件订阅,可以在目标机器上注册一个关机前触发的事件消费者。这个操作稍微复杂,但一次注册长期有效。
import wmi def register_shutdown_script(ip, username, password, script_path): """ 在目标机器注册关机前执行的脚本 script_path: 目标机器上的本地脚本路径,如 C:\\cleanup.bat """ conn = wmi.WMI(computer=ip, user=username, password=password) # 查询已存在的消费者,避免重复注册 existing = conn.Win32_SystemStartup() # 创建命令执行消费者 startup = conn.Win32_SystemStartup.new() # 绑定到关机事件 # 注意:这里用 Win32_SystemShutdown 事件类 consumer = conn.Win32_SystemShutdown.new() # 实际绑定需要调用 __EventFilter 和 __EventConsumer # 简化写法:直接用 Win32_Process 在关机前启动脚本 # 更稳妥的方式是用组策略或计划任务 print(f"请在 {ip} 上手动创建关机计划任务,触发条件选“关机时”")上面这段代码其实是个半成品,因为 WMI 事件订阅的完整实现需要操作__EventFilter、__EventConsumer和__FilterToConsumerBinding三个类,代码量不小。我一般直接建议用 Windows 自带的计划任务:在目标机器上创建一个任务,触发器选“当计算机即将关闭时”,操作指向你的清理脚本。这个用schtasks命令就能远程创建:
schtasks /create /s 192.168.1.101 /u Administrator /p Pass123! /tn "PreShutdownCleanup" /tr "C:\cleanup.bat" /sc onshutdown /f/sc onshutdown就是关机时触发,/f强制覆盖同名任务。这个命令比写 WMI 事件订阅简单得多,而且稳定。
验证方法:在目标机器上手动执行shutdown /s /t 60,然后观察C:\cleanup.bat是否在关机前运行。可以先把脚本内容改成写一个时间戳文件,确认触发时机。
从那以后我每次做批量关机前,都会先拿一台测试机跑一遍完整流程,确认凭据、防火墙、脚本触发都正常,再推到生产环境。希望帮到你。
本文还有配套的精品资源,点击获取