资讯详情 Python批量远程关机脚本:Windows局域网运维实战
📅 2026/10/23 20:26:38
简介这是一份面向IT运维人员与Python学习者的Windows远程批量关机脚本资源针对多台设备统一关机、故障排查与日常维护场景提供可直接参考的自动化实现思路。压缩包共3个文件以1个py脚本和2个txt说明文档为主脚本承载核心的WMI远程连接与关机逻辑txt则用于记录IP列表与使用说明整体仅923B轻量易读。资源围绕WMI服务与win32com.client模块展开演示了连接远程计算机、调用Win32_ComputerSystem的Shutdown方法、遍历IP列表批量下发关机命令的完整流程并涉及软关机与强制关机的参数差异、管理员权限要求、目标机远程管理配置以及异常捕获与重试等健壮性处理。目前已有897人学习下载适合希望快速掌握Windows远程管理脚本编写、提升批量设备维护效率的读者参考借鉴。1. 从一台台手动关机到一行命令搞定这个批量远程关机脚本到底能干什么机房巡检结束几十台 Windows 机器还亮着一台台点开始菜单关机显然不现实。这个项目就是冲着这个场景来的用 Python 在 Windows 下批量远程关闭局域网内的机器核心依赖系统自带的shutdown命令和net use建立 IPC 连接不需要在目标机器上装任何客户端。它适合谁手里管着几台到几十台 Windows 测试机、实验室公用机、渲染农场的开发者或运维尤其是那种“没有域控、没有专业运维平台但就是想让机器按时关掉”的小团队。整个脚本不依赖第三方库纯标准库加系统命令拿到就能改改完就能跑这也是我当初愿意拆它的原因——没有黑匣子每一行都能看懂。2. 环境准备与核心依赖跑通之前先把这几件事确认了2.1 运行环境与权限要求这个脚本对运行环境的要求其实不低但都是 Windows 自带的机制不需要额外装东西。首先运行脚本的机器必须是 Windows因为shutdown和net use都是 Windows 命令Linux 下没有对应实现。Python 版本建议 3.6 以上用到了 f-string 和subprocess.run的capture_output参数。权限方面当前登录账户必须对目标机器有管理员权限否则net use建立 IPC 连接时会直接返回“拒绝访问”。常见做法是运行脚本的账户和目标机器的本地管理员账户同名同密码或者目标机器开启了相同的凭据缓存。如果目标机器在域里直接用域账户跑就行如果是工作组环境就得在每台目标机器上提前建好同名账户。提示工作组环境下目标机器的防火墙需要放行“文件和打印机共享”以及“Windows Management Instrumentation”相关规则否则net use连不上。2.2 目标机器需要开启的服务与端口远程关机依赖两个关键点一是 IPC 连接能建立二是shutdown命令能通过远程过程调用发过去。IPC 连接走的是 SMB 协议默认端口 445。shutdown命令远程执行时走的是 RPC动态端口范围通常在 49152 到 65535 之间。很多“连不上”的问题最后查下来都是 445 端口被防火墙拦了或者目标机器的“Server”服务没启动。我一般会先在目标机器上跑一遍net share确认 IPC$ 共享存在再用telnet 目标IP 445测一下端口通不通。如果目标机器是 Windows 10 之后的版本默认可能关闭了 SMB1但 IPC$ 走的是 SMB2/3不受影响不用特意去开 SMB1。2.3 依赖清单与目录结构项目本身没有requirements.txt因为不需要装任何第三方包。整个目录结构通常是这样remote_shutdown/ ├── shutdown_batch.py # 主脚本 ├── hosts.txt # 目标机器列表一行一个IP或主机名 └── logs/ └── shutdown.log # 运行日志记录每台机器的执行结果hosts.txt是纯文本每行一个目标支持 IP 也支持主机名空行和#开头的注释行会被跳过。logs目录如果不存在脚本里用os.makedirs自动创建。这种“一个脚本加一个列表文件”的结构好处是改目标不用动代码直接编辑文本就行适合非开发人员接手。3. 核心模块拆解从读取列表到远程关机的完整链路3.1 读取目标列表与参数解析脚本入口先处理命令行参数支持指定列表文件路径、超时时间、是否强制关机。用argparse标准库不引入额外依赖。读取列表时做一次清洗去掉空白和注释。import argparse import os def parse_args(): parser argparse.ArgumentParser(descriptionWindows 批量远程关机) parser.add_argument(-f, --file, defaulthosts.txt, help目标机器列表文件默认 hosts.txt) parser.add_argument(-t, --timeout, typeint, default10, help单台机器连接超时秒数默认 10) parser.add_argument(--force, actionstore_true, help强制关闭不等待应用程序保存) return parser.parse_args() def load_hosts(path): if not os.path.exists(path): raise FileNotFoundError(f列表文件不存在: {path}) hosts [] with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue hosts.append(line) return hostsparse_args里三个参数各有用途--file让不同项目可以维护各自的机器列表--timeout控制单台机器的等待上限避免某台离线机器拖慢整体进度--force对应shutdown命令的/f参数强制结束未保存的程序。load_hosts里做了两件事检查文件存在性以及过滤空行和注释。这里用utf-8编码读取是因为有些机器名可能包含中文虽然不常见但加上更稳妥。3.2 建立 IPC 连接与执行关机命令这是整个脚本最核心的部分。Windows 下远程关机不能直接对 IP 执行shutdown必须先通过net use建立到目标机器 IPC$ 的认证连接然后才能发关机指令。连接和关机都通过subprocess.run调用系统命令完成。import subprocess import logging def connect_ipc(host, timeout): 建立到目标机器 IPC$ 的连接 cmd [net, use, f\\\\{host}\\IPC$, /user:Administrator] try: result subprocess.run(cmd, capture_outputTrue, textTrue, timeouttimeout) if result.returncode ! 0: # 如果已经存在连接net use 会报错但实际可用 if 已存在 in result.stderr or already in result.stderr.lower(): return True logging.warning(f{host} IPC连接失败: {result.stderr.strip()}) return False return True except subprocess.TimeoutExpired: logging.error(f{host} 连接超时({timeout}s)) return False def remote_shutdown(host, forceFalse): 向目标机器发送关机指令 cmd [shutdown, /s, /t, 0, /m, f\\\\{host}] if force: cmd.append(/f) try: result subprocess.run(cmd, capture_outputTrue, textTrue, timeout15) if result.returncode 0: logging.info(f{host} 关机指令已发送) return True logging.error(f{host} 关机失败: {result.stderr.strip()}) return False except subprocess.TimeoutExpired: logging.error(f{host} 关机命令超时) return Falseconnect_ipc里的/user:Administrator是硬编码的实际使用时需要改成目标机器上存在的管理员账户。如果目标机器账户不同可以在hosts.txt里用IP,用户名,密码的格式扩展但原项目没做这么复杂保持简单。net use返回非零不一定代表失败如果之前已经建立过连接它会提示“已存在”这种情况直接当作成功处理。remote_shutdown里的/t 0表示立即关机/m \\\\host指定目标机器。注意shutdown命令的/m参数要求当前账户对目标机器有管理员权限且目标机器的远程注册表服务可访问。3.3 日志记录与结果汇总每台机器的执行结果都要落盘方便事后排查哪台没关成功。日志用标准库logging同时输出到文件和控制台。def setup_logging(log_dirlogs): os.makedirs(log_dir, exist_okTrue) log_file os.path.join(log_dir, shutdown.log) logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, handlers[ logging.FileHandler(log_file, encodingutf-8), logging.StreamHandler() ] ) def main(): args parse_args() setup_logging() hosts load_hosts(args.file) success, fail 0, 0 for host in hosts: if connect_ipc(host, args.timeout): if remote_shutdown(host, args.force): success 1 else: fail 1 else: fail 1 logging.info(f执行完成: 成功 {success} 台, 失败 {fail} 台)setup_logging里同时挂了FileHandler和StreamHandler这样跑的时候能看到实时输出跑完还能翻日志。main函数里对每台机器先连 IPC 再发关机指令两步都成功才算成功。最后汇总成功和失败的数量。这里没有做并发因为关机操作本身很快几十台机器串行也就一两分钟并发反而容易触发目标机器的连接数限制。4. 避坑与常见问题这几处翻车点我替你踩过了4.1 报“拒绝访问”但账户密码明明是对的现象net use返回“系统发生 5 错误拒绝访问”。原因通常有三个一是目标机器的本地安全策略里“账户使用空白密码的本地账户只允许进行控制台登录”被启用了空密码账户无法远程连接二是目标机器的“网络访问本地账户的共享和安全模型”设置成了“仅来宾”导致管理员账户也被降权三是当前账户和目标机器账户同名但密码不同。解决在目标机器上把空密码策略禁用把共享安全模型改成“经典”或者统一账户密码。4.2 关机命令返回成功但机器没关现象shutdown命令返回码为 0日志显示“关机指令已发送”但目标机器依然亮着。原因一般是目标机器上有程序阻止了关机而脚本没加/f参数。Windows 默认会给应用程序最多 5 秒的保存时间如果有程序弹窗询问是否保存关机就会被挂起。解决运行脚本时加上--force对应shutdown的/f参数强制结束进程。但要注意强制关机可能导致未保存的数据丢失生产环境慎用。4.3 部分机器连不上但 ping 得通现象ping目标 IP 正常但net use超时或报错。原因通常是目标机器的 445 端口被防火墙拦截或者“Server”服务被禁用。Windows 防火墙默认对非域网络会阻止文件和打印机共享。解决在目标机器上执行netsh advfirewall firewall set rule group文件和打印机共享 new enableYes或者手动放行 445 端口。如果目标机器是 Windows 10 家庭版可能本身就不支持完整的 SMB 服务端功能这种情况只能换方案。4.4 脚本跑完残留 IPC 连接现象多次运行脚本后net use列表里堆积了大量到不同机器的 IPC$ 连接导致后续连接报“已达到计算机的连接数最大值”。原因net use建立的连接默认会保持直到会话结束或手动断开。解决在remote_shutdown成功后加一句subprocess.run([net, use, f\\\\{host}\\IPC$, /delete, /y])主动断开。或者在脚本开头统一清理一次net use * /delete /y但这样会断开所有已有连接需要评估影响。4.5 目标机器关机后脚本卡住现象脚本在发送关机指令后长时间无响应最后超时。原因shutdown命令在目标机器开始关机流程后RPC 连接可能不会立即返回导致subprocess.run等待。解决给remote_shutdown里的subprocess.run设置一个较短的timeout比如 15 秒超时后直接当作成功处理因为指令已经发出去了。或者用subprocess.Popen不等待返回但这样拿不到返回码需要权衡。5. 二次开发与改造思路从能跑到好用还差这几步5.1 支持多账户与凭据文件原脚本把用户名硬编码成Administrator实际环境里目标机器的管理员账户可能各不相同。改造思路是在hosts.txt里支持IP,用户名,密码的格式读取时解析成字典connect_ipc根据每台机器的凭据动态拼接net use命令。密码不建议明文写在列表里可以用keyring库存到 Windows 凭据管理器或者用一个单独的加密配置文件。我一般会加一个--cred-file参数指向一个 JSON 文件格式是{192.168.1.10: {user: admin, pass: xxx}}脚本启动时加载到内存不落盘明文。5.2 加入并发与超时控制串行执行在几十台机器时还能接受上百台就明显慢了。可以用concurrent.futures.ThreadPoolExecutor做并发每个线程处理一台机器主线程汇总结果。但要注意两点一是并发数不要超过目标网络能承受的连接数一般设 10 到 20 个线程二是net use命令本身不是线程安全的多个线程同时操作 IPC 连接可能冲突稳妥做法是每台机器独立连接、独立断开不共享连接。下面是一个改造后的并发骨架from concurrent.futures import ThreadPoolExecutor, as_completed def shutdown_one(host, timeout, force): if connect_ipc(host, timeout): ok remote_shutdown(host, force) # 主动断开避免连接堆积 subprocess.run([net, use, f\\\\{host}\\IPC$, /delete, /y], capture_outputTrue) return host, ok return host, False def main_concurrent(hosts, timeout, force, max_workers10): results [] with ThreadPoolExecutor(max_workersmax_workers) as pool: futures {pool.submit(shutdown_one, h, timeout, force): h for h in hosts} for future in as_completed(futures): host, ok future.result() results.append((host, ok)) logging.info(f{host} {成功 if ok else 失败}) return resultsmax_workers控制并发上限默认 10 个线程。每个线程里先连 IPC再发关机指令最后主动断开连接。as_completed按完成顺序收集结果不用等所有线程都跑完才处理。这样改造后100 台机器的执行时间大概能从几分钟压缩到十几秒。5.3 增加关机前检查与延迟关机直接关机太粗暴有时候目标机器上还有人在用。可以在关机前加一个检查步骤比如通过 WMI 查询目标机器是否有活跃的交互式登录会话或者检查 CPU 占用率是否低于某个阈值。Python 里可以用wmi库需要pywin32远程查询但这就引入了第三方依赖。更轻量的做法是先用ping确认机器在线再用tasklist /s \\\\host /u user /p pass查看是否有特定进程在跑比如chrome.exe或notepad.exe。如果检测到活跃进程就跳过这台机器记录到日志里等下一轮再处理。延迟关机可以用shutdown /s /t 300实现给用户 5 分钟缓冲时间同时发一条msg消息通知。5.4 验证改造效果的方法改完之后怎么确认没问题我一般分三步验证。第一步用一台测试机跑单机模式确认net use和shutdown命令能正常执行日志里能看到成功记录。第二步用三到五台机器跑并发模式观察日志里每台机器的状态确认没有连接冲突和超时。第三步故意把其中一台机器的 IP 改成不存在的地址看脚本是否能正确记录失败并继续处理其他机器而不是整个卡住。最后检查net use列表确认脚本跑完后没有残留连接。这套流程走下来基本能覆盖大部分边界情况。从那以后我每次改完这类远程操作脚本都会先在一台闲置机器上跑一遍完整流程确认关机、日志、连接清理三个环节都正常再放到正式环境。远程操作这东西翻车往往不是因为代码逻辑错而是环境差异和权限细节没对齐。希望帮到你。本文还有配套的精品资源点击获取