简介这份资源面向需要在Windows环境下管理多台设备的IT运维人员与Python学习者提供一套自动远程批量关机的脚本方案解决逐台手动关机效率低、易遗漏的问题。压缩包共3个文件包含1个py脚本与2个txt说明文档整体仅923B轻量易取脚本可直接运行或按需修改说明文档则用于交代使用方式与注意事项。核心思路是借助win32com.client模块连接WMI服务通过Win32_ComputerSystem的Shutdown方法对IP列表逐台下发关机指令并支持软关机与强制关机两种模式同时涉及管理员权限、目标机远程管理策略配置等关键前提。读者可据此掌握远程连接、批量遍历、异常捕获与重试超时等实用写法并了解将账号密码等敏感信息存入环境变量或加密配置的做法。目前已有897人学习下载适合希望提升多机维护效率的运维人员参考。1. 从一台台点关机到一行命令收工这套 Python 批量远程关机脚本到底能干什么机房巡检结束、实验室工位下班、测试机架跑完通宵任务最烦的不是活干完了而是几十台 Windows 机器还亮着屏等你一台台去点关机。远程桌面挨个连、开始菜单点关机、等它转圈一套下来半小时没了。这套「Python Windows 原生远程关机」的方案核心就是用 Python 把shutdown命令和 WMI 调用串起来读一份机器清单批量下发关机指令支持延时、强制关闭、取消关机、结果回执。它不依赖任何第三方远程控制软件走的是 Windows 自带的 RPC/WMI 通道适合手里有 10 到 200 台同域或同网段 Windows 机器的运维、测试和实验室管理场景。下面按「原理 → 环境 → 代码 → 排错 → 进阶」的顺序拆开讲每一步都能直接抄。2. 原理与选型为什么是 shutdown 命令加 WMI而不是自己写 socket2.1 Windows 远程关机的三条路各自适合谁在 Windows 上让另一台机器关机常见做法有三条一是shutdown /s /m \\目标IP二是 WMI 的Win32_OperatingSystem.Shutdown()三是自己写 socket 服务端监听指令。第三条最灵活但要每台机器装常驻进程维护成本高不适合「临时批量关一批机器」这种诉求。前两条都是系统原生能力区别在于shutdown /m走的是 RPC依赖目标机的 Remote Registry 和 Server 服务以及调用方在目标机上的管理员权限。它的好处是命令简单、返回码明确、支持/t延时和/c注释。缺点是跨网段、跨域时容易因为名称解析或防火墙翻车。WMI 走的是 DCOM/RPC 135 端口加动态端口通过Win32_OperatingSystem类的Shutdown方法触发。它比shutdown命令更「重」但能拿到目标机的系统信息、能区分关机还是重启适合需要先探测再操作的场景。我一般会两条腿走路优先用shutdown /m因为它快、返回码好判断当目标机名称解析有问题或者需要先确认系统状态时退回 WMI。下面代码里两条路都给了。2.2 前置条件三件事没搞定代码写得再漂亮也白搭第一调用方要有目标机的管理员凭据。如果是域环境用域管理员账号如果是工作组两台机器的本地管理员用户名密码要一致或者用net use \\目标IP\IPC$ /user:管理员 密码先建立会话。第二目标机要放行。防火墙里「文件和打印机共享」和「Windows Management Instrumentation (WMI)」两条入站规则要开。命令行快速开netsh advfirewall firewall set rule groupWindows Management Instrumentation (WMI) new enableyes netsh advfirewall firewall set rule group文件和打印机共享 new enableyes第三目标机的 Remote Registry 服务要是运行状态。很多精简版系统默认把它禁了shutdown /m会直接报「拒绝访问」。用sc query RemoteRegistry确认不是 RUNNING 就sc config RemoteRegistry start auto net start RemoteRegistry。提示这三条在域环境里通常由组策略统一配好工作组环境才需要逐台确认。先拿一台机器验证通了再铺开到全部。2.3 机器清单怎么组织一个 txt 就够别上数据库批量操作最忌讳把目标写死在代码里。我习惯用一个纯文本清单一行一个目标支持 IP 和主机名混写行内用#注释。这样换一批机器只改文件不改代码。清单格式# 机房A区 192.168.10.11 192.168.10.12 # 测试机延时短一点 lab-pc-03 # 机房B区 192.168.20.5读取时跳过空行和注释行这个逻辑放在代码里。清单文件建议和脚本放同一目录用相对路径引用避免换机器后路径失效。3. 动手实现从单机验证到批量下发的完整脚本3.1 先做单机验证别一上来就批量批量脚本翻车最惨的情况是参数写错把不该关的机器全关了。所以第一步永远是拿一台机器验证命令本身通不通。打开 CMD手动跑一条shutdown /s /m \\192.168.10.11 /t 60 /c test shutdown /f参数逐个说清楚/s是关机/r是重启/m \\目标指定远程机器/t 60是 60 秒后执行/c后面是给目标机用户看的提示文字/f强制关闭未保存的程序。如果这条命令返回「拒绝访问(5)」说明权限或服务没配好回到 2.2 排查返回「找不到网络路径(53)」多半是名称解析或防火墙。单机通了再往下。3.2 核心脚本读清单、批量下发、收集返回码下面这份脚本用subprocess调shutdown用concurrent.futures做并发避免一台台串行等。返回码统一收集最后打印成功失败清单。import subprocess import concurrent.futures from pathlib import Path # 读取机器清单跳过空行和 # 注释行 def load_targets(list_file): targets [] for line in Path(list_file).read_text(encodingutf-8).splitlines(): line line.split(#)[0].strip() # 去掉行内注释 if line: targets.append(line) return targets # 对单台机器下发关机指令返回 (目标, 返回码, 输出) def shutdown_one(target, delay60, forceTrue, commentbatch shutdown): cmd [shutdown, /s, f/m, f\\\\{target}, /t, str(delay), /c, comment] if force: cmd.append(/f) # 强制关闭未保存程序 try: # timeout 防止目标机无响应时卡死整个批次 r subprocess.run(cmd, capture_outputTrue, textTrue, timeout15) return target, r.returncode, (r.stderr or r.stdout).strip() except subprocess.TimeoutExpired: return target, -1, timeout def main(): targets load_targets(targets.txt) print(f共 {len(targets)} 台目标) # 并发数别开太大10 左右比较稳避免把网络和认证打爆 with concurrent.futures.ThreadPoolExecutor(max_workers10) as pool: futures [pool.submit(shutdown_one, t) for t in targets] for f in concurrent.futures.as_completed(futures): target, code, msg f.result() status OK if code 0 else fFAIL({code}) print(f{status:10} {target:20} {msg}) if __name__ __main__: main()逻辑说明load_targets负责清洗清单shutdown_one把参数拼成列表传给subprocess用列表而不是字符串是为了避免路径里的反斜杠被 shell 二次转义——这是 Windows 下拼命令最容易踩的坑。timeout15是关键目标机如果网络不通shutdown可能卡很久没有超时整个批次会被一台机器拖死。max_workers10是经验值并发太高会触发目标机的认证限流反而大面积失败。参数说明delay默认 60 秒给目标机用户留出保存时间生产环境如果确认没人用可以设成 0 立即关。forceTrue对应/f会强制结束未保存的程序慎用——如果目标机上有正在写数据库的任务强制关可能丢数据。comment会显示在目标机的关机提示框里写清楚来源方便对方知道是谁在操作。3.3 加一个取消关机的后悔药批量下发后经常发现清单里混进了不该关的机器这时候/a能救场。取消关机的脚本和上面几乎一样只改命令def abort_one(target): # /a 取消关机注意它不接受 /m需要在目标机上执行 # 远程取消用 shutdown /a /m \\目标 在部分版本不生效稳妥做法是 WMI 或 psexec cmd [shutdown, /a, f/m, f\\\\{target}] r subprocess.run(cmd, capture_outputTrue, textTrue, timeout15) return target, r.returncode, (r.stderr or r.stdout).strip()这里有个血泪经验shutdown /a /m在 Windows 10 之后的某些版本上对远程目标不生效返回「无法中止系统关机因为没有进行中的关机操作」。真正可靠的远程取消是走 WMI 调Win32_OperatingSystem的AbortShutdown或者用psexec \\目标 shutdown /a在目标机本地执行。所以延时别设太短给自己留出反悔窗口。4. 避坑与排查五条真实踩过的坑4.1 现象部分机器返回「拒绝访问(5)」但手动 RDP 能登原因RDP 登录用的是交互式登录走的是另一套权限shutdown /m走的是网络登录需要目标机的本地管理员权限且 UAC 远程限制LocalAccountTokenFilterPolicy会拦掉工作组环境下的管理员令牌。解决在目标机注册表加一条允许远程用本地管理员完整令牌。命令行reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f域环境一般不需要工作组环境基本必加。4.2 现象脚本卡住不动某台机器一直没返回原因目标机离线或网络不通shutdown在等 RPC 超时默认超时可能长达几十秒。解决subprocess.run必须带timeout捕获TimeoutExpired后标记为失败继续下一台。上面代码里已经处理。另外可以在下发前先ping -n 1 -w 1000 目标做一次存活探测离线的直接跳过省得白等。4.3 现象清单里主机名能 ping 通但 shutdown 报「找不到网络路径(53)」原因主机名解析到了 IPv6 地址而目标机的 RPC 只监听 IPv4或者 DNS 里有多条 A 记录指向了错误的机器。解决清单里优先写 IP或者在下发前用socket.gethostbyname强制解析成 IPv4 再拼命令。如果必须用主机名检查ping 主机名返回的是不是预期的 IP。4.4 现象目标机弹出关机提示但 60 秒后没关反而重启了原因命令里/s和/r写混了或者目标机有未完成的 Windows Update 挂起重启系统优先执行了更新重启。解决下发前用 WMI 查一下目标机是否有 pending reboot有的话先处理更新。命令拼装时把/s和/r做成互斥参数别让调用方随便传。4.5 现象并发开到 50结果一半机器失败报「RPC 服务器不可用」原因短时间内大量并发认证请求把目标机的 RPC 或安全账户管理器打限流了尤其是工作组环境下每台都要独立认证。解决并发数控制在 10 到 15批次之间加time.sleep(1)做节流。如果机器数量超过 100分批跑每批 30 台批间停 5 秒。别贪快稳定比速度重要。5. 进阶把脚本做成可复用工具的几个技巧5.1 用配置文件管参数别改代码把延时、并发数、是否强制、清单路径抽到一个config.ini里用configparser读。这样不同场景换配置就行代码不动。配置示例[shutdown] list_file targets.txt delay 60 force true max_workers 10 comment scheduled maintenance读取时给每个参数设默认值配置文件缺项也不报错。这个习惯在批量运维脚本里特别值因为参数经常要按批次调。5.2 结果落盘方便事后核对控制台打印看完就没了批量操作一定要把结果写文件。用csv模块把「目标、返回码、消息、时间戳」写进result_时间戳.csv出问题能回溯是哪台、什么时候、什么原因失败。格式建议字段含义示例target目标机192.168.10.11code返回码0message系统返回成功timestamp下发时间2025-01-01 18:00:00返回码对照0 成功5 拒绝访问53 找不到网络路径1231 网络不可达-1 脚本超时。把这几个码记熟排查时一眼能定位方向。5.3 验证方法怎么确认机器真的关了下发成功不等于机器关了。延时期间用户可能点了取消或者机器卡在关机流程。验证手段有两个一是延时结束后ping目标不通说明大概率关了二是用 WMI 查Win32_OperatingSystem的LastBootUpTime如果查不到说明已关机。我一般在下发后等delay 30秒再跑一轮 ping 探测把还通的机器单独列出来人工确认。5.4 一个容易忽略的细节关机前先确认没有人在用批量关机最怕关掉别人正在用的机器。稳妥做法是下发前用query user /server:目标查一下有没有活跃会话有会话的机器单独拎出来延时设长一点或者跳过。命令query user /server:192.168.10.11返回有用户名说明有人在用返回「没有用户存在」才安全。这一步多花几秒能避免很多扯皮。从那以后我每次跑批量关机都强制走一遍「单机验证 → 清单核对 → 活跃会话检查 → 小批量试跑 → 全量下发」这五步哪怕再急也不跳。批量操作没有后悔药延时和清单是仅有的两道保险。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?