機:Windows局域網(wǎng)多機自動關(guān)機腳本實戰(zhàn))
簡介這份資源面向需要在Windows環(huán)境下管理多臺設(shè)備的IT運維人員與Python學(xué)習(xí)者提供一套自動遠程批量關(guān)機的腳本方案解決逐臺手動關(guān)機效率低、易遺漏的問題。壓縮包共3個文件包含1個py腳本與2個txt說明文檔整體僅923B輕量易取腳本可直接參考或改造使用。內(nèi)容圍繞WMI遠程管理展開涉及通過win32com.client建立遠程連接、調(diào)用Win32_ComputerSystem的Shutdown方法執(zhí)行軟關(guān)機或強制關(guān)機并以IP列表遍歷實現(xiàn)批量下發(fā)命令同時提示管理員權(quán)限、目標(biāo)機遠程管理策略配置以及用戶名密碼的安全存放方式。已有897人學(xué)習(xí)下載適合希望快速掌握遠程批量關(guān)機思路、并在此基礎(chǔ)上擴展重試與超時等健壯性處理的讀者參考借鑒。1. 機房巡檢收尾那點事為什么我最終選了 Python 批量遠程關(guān)機凌晨一點半實驗室的燈還亮著三十多臺 Windows 工作站跑完最后一輪渲染任務(wù)風(fēng)扇聲像一群不肯落地的直升機。手動一臺臺點“開始→關(guān)機→確定”點到最后手都麻了還漏了兩臺第二天被導(dǎo)師問“昨晚那幾臺怎么還開著”。這就是我最初折騰批量遠程關(guān)機的真實動機——不是炫技是實在受不了重復(fù)勞動。這份資源要解決的核心問題很具體在 Windows 環(huán)境下用 Python 腳本對局域網(wǎng)內(nèi)多臺 Windows 主機發(fā)起自動、批量的遠程關(guān)機。它適合誰適合手里管著幾臺到幾十臺 Windows 機器、又不想逐臺登錄操作的人——實驗室管理員、測試機維護者、小型機房值守人員。關(guān)鍵詞里的“自動”“遠程關(guān)機”“批量關(guān)機”“windows”“python”五個詞基本就是它的全部能力邊界。下面我按“先跑通一臺再擴到一批最后處理翻車”的順序拆開講。2. 遠程關(guān)機的底層通道WMI、RPC 與 shutdown 命令怎么選2.1 三條常見技術(shù)路線對比Windows 上讓另一臺機器關(guān)機繞不開三個東西shutdown命令、WMIWindows Management Instrumentation、以及基于 RPC 的遠程調(diào)用。它們不是并列關(guān)系而是層層包裹——shutdown /m底層走的就是 RPCWMI 也能調(diào)Win32_OperatingSystem的Shutdown方法。路線調(diào)用方式依賴服務(wù)適用場景主要限制shutdown 命令shutdown /s /m \\目標(biāo)IPRPC、Remote Registry快速腳本、臨時操作報錯信息模糊權(quán)限要求高WMIwmic /node:IP或 Pythonwmi庫WMI 服務(wù)、DCOM需要讀取狀態(tài)再關(guān)機配置復(fù)雜防火墻端口多PsExec 類工具遠程執(zhí)行進程Admin$ 共享、SMB需要執(zhí)行任意命令需額外工具易被殺軟攔截我一般優(yōu)先用shutdown命令因為它最輕不需要額外裝庫Python 里一個subprocess就能調(diào)。但它的坑也最多后面避坑章節(jié)會細說。2.2 為什么用 Python 而不是批處理批處理也能寫for循環(huán)調(diào)shutdown但一旦要處理“哪些機器在線”“失敗重試”“記錄日志”“并發(fā)控制”bat 就力不從心了。Python 的優(yōu)勢在于可以用concurrent.futures做并發(fā)用subprocess拿返回碼用logging落日志用ipaddress生成網(wǎng)段。這些在幾十臺機器的場景下不是錦上添花是剛需——串行關(guān)機等一臺超時 30 秒三十臺就是十五分鐘。2.3 前置條件目標(biāo)機器必須打開什么遠程關(guān)機不是發(fā)個包就完事目標(biāo) Windows 主機必須滿足幾個條件缺一個就報“拒絕訪問”Remote Registry 服務(wù)默認手動啟動遠程 RPC 調(diào)用需要它。設(shè)為自動并啟動。防火墻規(guī)則入站放行“Windows Management Instrumentation (WMI)”和“Remote Service Management”相關(guān)規(guī)則或者臨時關(guān)閉域內(nèi)防火墻測試。賬號權(quán)限發(fā)起端使用的賬號必須是目標(biāo)機的管理員組成員且目標(biāo)機不能是空密碼賬號Windows 默認禁止空密碼遠程登錄。網(wǎng)絡(luò)發(fā)現(xiàn)同一網(wǎng)段或路由可達能 ping 通是基本前提。這些條件在域環(huán)境里通常由組策略統(tǒng)一配好但在工作組環(huán)境比如實驗室臨時搭的機器里得一臺臺確認。我習(xí)慣先拿一臺當(dāng)“小白鼠”跑通了再批量。import subprocess def shutdown_one(ip, timeout10): 對單臺 Windows 主機發(fā)起遠程關(guān)機 :param ip: 目標(biāo)主機 IP :param timeout: 子進程超時秒數(shù)防止卡死 :return: (ip, 是否成功, 返回信息) cmd fshutdown /s /t 0 /m \\\\{ip} /f try: # capture_output 捕獲 stdout/stderrtext 讓輸出為字符串 result subprocess.run( cmd, shellTrue, capture_outputTrue, textTrue, timeouttimeout ) if result.returncode 0: return ip, True, 關(guān)機指令已發(fā)送 else: return ip, False, result.stderr.strip() or result.stdout.strip() except subprocess.TimeoutExpired: return ip, False, 超時目標(biāo)無響應(yīng)這段代碼是整套腳本的最小單元。shutdown /s表示關(guān)機/t 0表示立即執(zhí)行延遲 0 秒/m \\IP指定遠程目標(biāo)/f強制關(guān)閉正在運行的程序——不加/f時如果有未保存的文檔目標(biāo)機會彈窗等待遠程場景下沒人點確認就會一直掛著。timeout參數(shù)是血淚經(jīng)驗網(wǎng)絡(luò)不通時shutdown命令可能卡很久不設(shè)超時整個批量任務(wù)會被一臺機器拖死。提示/t 0和/f組合會直接丟棄未保存內(nèi)容生產(chǎn)環(huán)境慎用測試機隨意。3. 從單臺到批量IP 列表、并發(fā)與日志三件套3.1 IP 來源文件讀取還是網(wǎng)段掃描批量關(guān)機的第一步是拿到目標(biāo)列表。常見做法有兩種一是維護一個hosts.txt每行一個 IP二是給一個網(wǎng)段先 ping 掃描再關(guān)機。前者可控后者省事但容易誤傷。我一般用文件方式因為“哪些機器該關(guān)”是個業(yè)務(wù)判斷不該交給腳本猜。文件格式就一行一個 IP允許#開頭注釋# 渲染組 192.168.1.101 192.168.1.102 # 測試組 192.168.1.201讀取時過濾空行和注釋順便去重def load_targets(path): 從文本文件加載 IP 列表忽略空行和 # 注釋 targets [] with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue targets.append(line) # 去重但保持順序 return list(dict.fromkeys(targets))dict.fromkeys是去重的常用技巧比set好的地方是保留原始順序日志讀起來符合直覺。3.2 并發(fā)控制線程池不是越多越好三十臺機器串行關(guān)機每臺哪怕只花 2 秒也要一分鐘。用線程池并發(fā)能把時間壓到幾秒。但線程數(shù)不是越大越好——shutdown命令本身會創(chuàng)建進程線程太多反而搶資源而且目標(biāo)機可能扛不住同時涌入的連接。from concurrent.futures import ThreadPoolExecutor, as_completed def shutdown_batch(targets, max_workers8): 并發(fā)批量關(guān)機max_workers 控制同時發(fā)起的關(guān)機數(shù) results [] with ThreadPoolExecutor(max_workersmax_workers) as executor: future_map { executor.submit(shutdown_one, ip): ip for ip in targets } for future in as_completed(future_map): ip future_map[future] try: results.append(future.result()) except Exception as e: results.append((ip, False, f異常: {e})) return resultsmax_workers8是我在幾十臺規(guī)模下比較穩(wěn)的值。機器少可以調(diào)到 4上百臺可以到 16但再高收益遞減還容易觸發(fā)目標(biāo)機的連接限制。as_completed保證誰先返回先處理誰不會因為某臺慢而阻塞日志輸出。3.3 日志與結(jié)果核對別關(guān)完就完事批量操作最怕“以為都關(guān)了其實有幾臺沒關(guān)”。所以每次執(zhí)行都要落日志記錄時間、IP、成功與否、失敗原因。日志用標(biāo)準(zhǔn)logging模塊同時輸出到控制臺和文件import logging def setup_logger(log_fileshutdown.log): logger logging.getLogger(batch_shutdown) logger.setLevel(logging.INFO) fmt logging.Formatter(%(asctime)s [%(levelname)s] %(message)s) # 文件處理器追加模式 fh logging.FileHandler(log_file, encodingutf-8) fh.setFormatter(fmt) # 控制臺處理器 ch logging.StreamHandler() ch.setFormatter(fmt) logger.addHandler(fh) logger.addHandler(ch) return logger執(zhí)行完打印一個匯總表成功多少、失敗多少、失敗的是哪些 IP。失敗列表可以直接復(fù)制出來排查后重跑——腳本要支持“只對失敗的重試”而不是每次全量重來。注意日志文件別放在會被關(guān)機影響的機器上發(fā)起端自己別被關(guān)進去。4. 避坑與排查遠程關(guān)機最常見的五類翻車4.1 現(xiàn)象提示“拒絕訪問(5)”原因發(fā)起端賬號不是目標(biāo)機管理員或目標(biāo)機禁用了空密碼遠程登錄或 Remote Registry 服務(wù)沒啟動。解決確認賬號在目標(biāo)機Administrators組用net use \\IP\IPC$ /user:管理員賬號先建立憑據(jù)會話再關(guān)機檢查組策略“賬戶使用空密碼的本地賬戶只允許進行控制臺登錄”是否被啟用。4.2 現(xiàn)象命令返回成功但機器沒關(guān)原因shutdown返回 0 只代表指令送達不代表目標(biāo)機執(zhí)行了。目標(biāo)機可能因為有待保存文檔、有程序阻止關(guān)機、或電源策略攔截。解決加/f強制關(guān)閉如果仍不關(guān)去目標(biāo)機事件查看器看“系統(tǒng)”日志里User32或Kernel-Power的記錄確認目標(biāo)機沒有處于“演示模式”或接了不允許關(guān)機的策略。4.3 現(xiàn)象部分機器超時腳本卡住原因網(wǎng)絡(luò)不通或目標(biāo)機防火墻攔截shutdown命令在等 RPC 響應(yīng)。解決subprocess.run必須帶timeout批量前先并發(fā) ping 一輪把不在線的剔掉減少無效等待。4.4 現(xiàn)象關(guān)機后無法再次遠程喚醒原因這是關(guān)機不是重啟Wake-on-LAN 需要網(wǎng)卡和主板支持且關(guān)機后網(wǎng)卡仍供電。解決如果后續(xù)要遠程開機關(guān)機前確認目標(biāo)機 BIOS 和網(wǎng)卡驅(qū)動開啟了 WOL否則只能現(xiàn)場開機。這個坑我在實驗室踩過關(guān)得爽第二天全員現(xiàn)場按電源鍵。4.5 現(xiàn)象殺軟或安全軟件攔截 shutdown 調(diào)用原因部分終端防護會把遠程shutdown當(dāng)成可疑行為。解決在目標(biāo)機安全軟件里把發(fā)起端 IP 或shutdown.exe加入白名單或者改用 WMI 方式但 WMI 同樣可能被攔。沒有銀彈只能按環(huán)境調(diào)。5. 進階把關(guān)機做成可調(diào)度、可回滾的例行任務(wù)5.1 用計劃任務(wù)做定時批量關(guān)機腳本跑通后下一步是讓它自動在固定時間執(zhí)行。Windows 任務(wù)計劃程序里建一個基本任務(wù)觸發(fā)器設(shè)為每天 23:30操作設(shè)為python.exe D:\scripts\batch_shutdown.py起始目錄填腳本所在目錄。注意“不管用戶是否登錄都要運行”要勾上否則鎖屏后不執(zhí)行。5.2 加一個“預(yù)演模式”再真關(guān)直接批量關(guān)機風(fēng)險高我后來給腳本加了一個--dry-run參數(shù)只 ping 和打印將要關(guān)機的列表不真正調(diào)用shutdown。確認列表無誤后再去掉參數(shù)執(zhí)行。這個習(xí)慣救過我好幾次——有一次 IP 文件里混進了一臺正在跑重要任務(wù)的機器預(yù)演時看到了及時刪掉。import argparse def parse_args(): parser argparse.ArgumentParser(descriptionWindows 批量遠程關(guān)機) parser.add_argument(--file, defaulthosts.txt, helpIP 列表文件) parser.add_argument(--workers, typeint, default8, help并發(fā)數(shù)) parser.add_argument(--dry-run, actionstore_true, help只預(yù)演不關(guān)機) return parser.parse_args()argparse讓腳本從“改代碼調(diào)參數(shù)”變成“命令行傳參”配合計劃任務(wù)和手動執(zhí)行都方便。--dry-run是布爾開關(guān)不加就是真關(guān)加了就只打印。5.3 驗證關(guān)機結(jié)果ping 輪詢確認指令發(fā)出去不等于關(guān)掉了。執(zhí)行完等 30 秒再對目標(biāo)列表做一輪 ping還在線的就是沒關(guān)成功的單獨列出來。這個“發(fā)指令→等待→驗證”的閉環(huán)比只看shutdown返回碼可靠得多。import subprocess def check_offline(ip): ping 一次返回 True 表示已離線關(guān)機成功 # -n 1 發(fā)一個包-w 1000 超時 1 秒 result subprocess.run( fping -n 1 -w 1000 {ip}, shellTrue, capture_outputTrue, textTrue ) return result.returncode ! 0ping返回非 0 通常意味著目標(biāo)不可達結(jié)合關(guān)機場景可以判定為已關(guān)機。但要注意有些機器關(guān)機后網(wǎng)卡仍響應(yīng) ARPping 可能還通這時以shutdown返回碼和事件日志為準(zhǔn)。從那以后我每次批量關(guān)機前都強制走一遍--dry-run確認列表、確認時間、確認沒有漏網(wǎng)的重要機器再真刀真槍執(zhí)行。希望幫到你。本文還有配套的精品資源點擊獲取