通过计算机列表批量升级Win11
在此前单机使用sc指令远程更新Win11大版本的技术上,引入计算机列表执行自动扫描、自动推送更新并保存失败列表的方案,
通过将控制流与数据流(TXT列表文件)彻底分离,你可以实现完全自动化的“流水线”式升级。
为了完美实现你的需求,我们需要创建或调整 3 个文件。它们全部放在控制端计算机的同一个文件夹下(例如 F:\Win11\ISO\):
list1.txt:你在开始前,把所有要检查的计算机名一行一个写在这里。0_Batch_Check.ps1(雷达探测脚本):自动读取list1.txt,测试通过的自动写入list2.txt。1_Auto_Upgrade.bat(自动化升级主控):无需人工手动输入,自动读取list2.txt逐台执行升级。如果哪台失败,自动记录到list3.txt。
以下是完整的配置文件与脚本代码:
准备工作:创建初始名单 list1.txt
在控制端脚本目录下新建一个 list1.txt,把目标计算机名敲进去,例如:
F202400507
F201900817
F202000903
第一步:深度探测并自动生成合格名单(PowerShell版)
请将以下代码保存为 0_Batch_Check.ps1。它会读取 list1.txt,并在筛出及格生后自动创建(或覆盖) list2.txt。
# ==================================================
# 保存为: 0_Batch_Check.ps1 (右键使用 PowerShell 运行)
# ==================================================
clear
\(ScriptDir = Split-Path -Parent\)MyInvocation.MyCommand.Definition
Set-Location \$ScriptDir
\(List1Path = "\)ScriptDir\list1.txt"
\(List2Path = "\)ScriptDir\list2.txt"
if (!(Test-Path \$List1Path)) {
Write-Error "找不到源计算机列表文件 list1.txt,请先创建它!"
Read-Host "按回车退出..."; exit
}
# 清空上一次的 list2.txt
if (Test-Path \(List2Path) { Remove-Item\)List2Path -Force }
\$ComputerList = Get-Content \(List1Path \vert{} Where-Object {\)_.Trim() -ne "" }
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " 自动化流水线 [第 1 步]: 批量读取 list1 深度扫描 " -ForegroundColor Cyan
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host "待检查总数: \((\)ComputerList.Count) 台`n"
[string]::Format("{0,-15} | {1,-10} | {2,-12} | {3,-10}", "计算机名/IP", "Ping状态", "445端口", "处理结果") | Write-Host -ForegroundColor White
Write-Host ("-" * 60)
$ReadyCount = 0
foreach ($Computer in $ComputerList) {
$Computer = $Computer.Trim()
$PingStatus = Test-Connection -ComputerName $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue
if ($PingStatus) {
$PortStatus = Test-NetConnection -ComputerName $Computer -Port 445 -WarningAction SilentlyContinue
if ($PortStatus.TcpTestSucceeded) {
# 完美符合要求,写入列表2
Add-Content -Path $List2Path -Value $Computer
[string]::Format("{0,-15} | {1,-10} | {2,-12} | {3,-10}", $Computer, " 在线", "开启", "已加入 list2.txt") | Write-Host -ForegroundColor Green
$ReadyCount++
continue
}
[string]::Format("{0,-15} | {1,-10} | {2,-12} | {3,-10}", $Computer, " 在线", "拦截", "防火墙阻挡,跳过") | Write-Host -ForegroundColor Yellow
} else {
[string]::Format("{0,-15} | {1,-10} | {2,-12} | {3,-10}", $Computer, "离线", " -- ", "未开机,跳过") | Write-Host -ForegroundColor Red
}
}
Write-Host "`n==================================================" -ForegroundColor Cyan
Write-Host " 扫描完成!共计 [ \$ReadyCount ] 台计算机成功写入 list2.txt" -ForegroundColor Green
Write-Host " 下一步:请双击运行 1_Auto_Upgrade.bat 开始无人值守升级。" -ForegroundColor Yellow
Write-Host "==================================================" -ForegroundColor Cyan
Read-Host "按任意键退出探测程序..."
请谨慎使用此类代码。
第二步:自动读取列表2群发升级,失败记入列表3(CMD 批处理版)
请将以下代码保存为 1_Auto_Upgrade.bat(确保使用 ANSI 编码保存)。它会完全自动读取 list2.txt,全程免交互自动推进。
@echo off
chcp 936 >nul
cls
:: ==========================================
:: 固化的基础配置信息
:: ==========================================
set SRC_ISO=\\F202300503\Win11\iso\Win11_26200.9445_noTPM_20260918.iso
set SRC_DIR=\\F202300503\Win11\iso
set ISO_NAME=Win11_26200.9445_noTPM_20260918.iso
set ADMIN_USER=administrator
set ADMIN_PASS=<你的管理员密码>
set LIST2=list2.txt
set LIST3=list3.txt
echo ==================================================
echo Windows 11 局域网流水线完全自动化升级控制台
echo ==================================================
echo.
if not exist "%LIST2%" (
echo [错误] 找不到就绪名单 %LIST2%,请先运行 0_Batch_Check.ps1!
pause
exit
)
:: 初始化或清空上一次的失败记录
echo :: 升级失败计算机名单 (生成时间: %date% %time%) > "%LIST3%"
echo 正在读取 %LIST2% 并准备开始全自动无人值守升级...
echo --------------------------------------------------
:: 核心循环:遍历读取列表2
for /f "tokens=*" %%i in (%LIST2%) do (
set "TARGET=%%i"
call :ProcessUpgrade "%%i"
)
echo.
echo ==================================================
echo 🎉 所有就绪计算机处理完毕!
echo 如果存在升级失败的机器,请查看 %LIST3%
echo ==================================================
pause
exit
:: ==========================================
:: 子程序:单台计算机升级逻辑
:: ==========================================
:ProcessUpgrade
set "CURR_TARGET=%~1"
echo.
echo [正在处理] ----------^> 目标机: %CURR_TARGET%
echo [1/4] 建立 IPC$ 管道...
net use \\%CURR_TARGET%\ipc$ "%ADMIN_PASS%" /user:%ADMIN_USER% >nul 2>nul
if errorlevel 1 (
echo [失败] 无法建立管道连接!
echo %CURR_TARGET% - 无法建立IPC管道连接 >> "%LIST3%"
goto :eof
)
echo [2/4] 创建远程 D:\Temp 目录...
if not exist "\\%CURR_TARGET%\d$\Temp" mkdir "\\%CURR_TARGET%\d$\Temp" >nul 2>nul
echo [3/4] 跨网络传输大文件 (Robocopy 传输中)...
robocopy "%SRC_DIR%" "\\%CURR_TARGET%\d$\Temp" "%ISO_NAME%" /Z /R:5 /W:5 >nul
if errorlevel 8 (
echo [失败] 文件复制过程中发生严重错误!
echo %CURR_TARGET% - Robocopy文件复制失败 >> "%LIST3%"
goto :eof
)
copy /Y "local_upgrade_final.bat" "\\%CURR_TARGET%\d$\Temp\" >nul
echo [4/4] 下发底层服务,隐形触发本地升级...
sc.exe \\%CURR_TARGET% create WinUpgrade binPath= "cmd.exe /c D:\Temp\local_upgrade_final.bat" start= demand type= own >nul 2>nul
sc.exe \\%CURR_TARGET% start WinUpgrade >nul 2>nul
if errorlevel 1 (
:: 注意:若sc start报1053错误,其实系统已经起跑了。此处主要抓无权或找不到服务的硬错误。
sc.exe \\%CURR_TARGET% query WinUpgrade 2>&1 | findstr /i "RUNNING START_PENDING" >nul
if errorlevel 1 (
echo [失败] 远程 SC 服务未能成功拉起脚本!
echo %CURR_TARGET% - SC远程触发失败 >> "%LIST3%"
sc.exe \\%CURR_TARGET% delete WinUpgrade >nul 2>nul
goto :eof
)
)
sc.exe \\%CURR_TARGET% delete WinUpgrade >nul 2>nul
echo System_Layout_OK: %CURR_TARGET% 远程部署成功,已隐形在后台起跑!
goto :eof
流水线标准作业流程
- 录入:把公司几百台计算机名往
list1.txt里一贴。 - 筛查:右键
0_Batch_Check.ps1-> 使用 PowerShell 运行。喝杯茶,等它刷完,完工后你会发现目录下多了一个list2.txt,里面全都是在线且防火墙打通的“优质目标”。 - 轰炸:直接双击
1_Auto_Upgrade.bat。它会像传送带一样,自动读list2.txt,第一台传完完工,立马自动跳到第二台。你完全不需要盯着屏幕守候。 - 收尾:全部结束后,如果有机器因为网络波动、中途关机或传输遭遇异常导致失败,直接打开
list3.txt,里面会清清楚楚写着是哪台机器、在哪个环节失败了,方便你精准售后。
批量抓取局域网内已开机的设备IP地址和MAC
这是基于上述批量更新的一个补充,针对已关机无法执行更新的设备通过日常扫描获取Mac地址,并实现唤醒,以便执行系统更新;使用纯文本控制的全自动化流水线部署系统目前已经完全契合你的生产环境。在开始对大批量的 list2.txt 统一群发前,建议先在 list1.txt 里只留 1-2 台测试机完整走一遍。
第一步:【资产录入】在开机时一键远程抓取 MAC(PowerShell 版)
请将以下代码保存为 0_Collect_MAC.ps1。当大批电脑开机在线时,右键运行它。它会自动读取 list1.txt,把所有在线机器的 MAC 远程抓下来存进 wol_database.csv:
# ==================================================
# 保存为: 0_Collect_MAC.ps1 (右键使用 PowerShell 运行)
# 作用: 远程批量抓取 list1.txt 中在线计算机的 MAC,生成本地资产库
# ==================================================
clear
ScriptDir = Split-Path -Parent MyInvocation.MyCommand.Definition
Set-Location \$ScriptDir
List1Path = "ScriptDir\list1.txt"
DbPath = "ScriptDir\wol_database.csv"
if (!(Test-Path \$List1Path)) {
Write-Error "找不到源计算机列表文件 list1.txt,请先创建它!"
Read-Host "按回车退出..."; exit
}
# 如果没有资产库,创建带表头的空库
if (!(Test-Path \$DbPath)) {
"ComputerName,MAC" | Out-File -FilePath \$DbPath -Encoding UTF8
}
\$ComputerList = Get-Content List1Path | Where-Object _.Trim() -ne "" }
Database = Import-Csv -Path DbPath -Encoding UTF8
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " 局域网 MAC 资产库自动采集/更新控制台 " -ForegroundColor Cyan
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host "正在扫描并收集,请稍候...`n" -ForegroundColor Yellow
foreach ($Computer in $ComputerList) {
$Computer = $Computer.Trim()
# 检查是否在线
$PingStatus = Test-Connection -ComputerName $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue
if (!$PingStatus) {
Write-Host "[$Computer] 当前离线,无法抓取 MAC。可在其开机时重新运行此脚本。" -ForegroundColor Red
continue
}
# 远程抓取活动网卡的 MAC 地址 (通过原生 CIM/WMI 远程调用,无视 wmic 被砍)
try {
$Mac = Get-CimInstance -ComputerName $Computer -ClassName Win32_NetworkAdapter -ErrorAction Stop |
Where-Object { $_.NetConnectionStatus -eq 2 -and $_.PhysicalAdapter -eq $true } |
Select-Object -ExpandProperty MACAddress -First 1
} catch {
$Mac = $null
}
if ($Mac) {
# 规范化 MAC 格式(统一转换为横杠分隔)
$Mac = $Mac.Replace(":", "-")
# 检查资产库中是否已存在该计算机
$Exist = $Database | Where-Object { $_.ComputerName -eq $Computer }
if (!$Exist) {
# 录入新资产
"$Computer,$Mac" | Out-File -FilePath $DbPath -Append -Encoding UTF8
Write-Host "[$Computer] 成功抓取 MAC: $Mac (已录入资产库)" -ForegroundColor Green
} else {
Write-Host "[$Computer] 资产库已存在记录,无需重复录入。" -ForegroundColor Gray
}
} else {
Write-Host "[$Computer] 在线,但未能远程获取到活动网卡 MAC 地址。" -ForegroundColor Yellow
}
}
Write-Host "`n==================================================" -ForegroundColor Cyan
Write-Host " 资产采集完成!数据已安全缓存在本地 wol_database.csv" -ForegroundColor Green
Write-Host "==================================================" -ForegroundColor Cyan
Read-Host "按任意键退出..."
第二步:【无脑唤醒】读取计算机名自动匹配唤醒(PowerShell 版)
当未来某些计算机进入睡眠或关机状态时,请右键运行以下脚本,保存为 0_Batch_WakeByName.ps1。它会自动读取 list1.txt,自动去上一步生成的资产库里匹配 MAC 并轰出唤醒封包,随后自动生成 list2.txt 合格升级名单:
# ==================================================
# 保存为: 0_Batch_WakeByName.ps1 (右键使用 PowerShell 运行)
# 作用: 读取 list1.txt,从本地资产库匹配 MAC 并一键唤醒筛选,输出 list2.txt
# ==================================================
clear
ScriptDir = Split-Path -Parent MyInvocation.MyCommand.Definition
Set-Location \$ScriptDir
List1Path = "ScriptDir\list1.txt"
List2Path = "ScriptDir\list2.txt"
DbPath = "ScriptDir\wol_database.csv"
if (!(Test-Path List1Path) -or !(Test-Path DbPath)) {
Write-Error "缺少 list1.txt 或本地资产库 wol_database.csv,请先检查!"
Read-Host "按回车退出..."; exit
}
\$ComputerList = Get-Content List1Path | Where-Object _.Trim() -ne "" }
Database = Import-Csv -Path DbPath -Encoding UTF8
function Send-WOLPacket ([string]\$MacAddress) {
cleanMac = MacAddress.Replace("-","").Replace(":","")
if (\$cleanMac.Length -ne 12) { return \(false }\)packet = [byte[]](,0xFF * 6)
for (\$i = 0; i -lt 16; i++) {
for (\$j = 0; j -lt 12; j += 2) { \(packet += [Convert]::ToByte(\)cleanMac.Substring(\(j, 2), 16) } }\)UDPClient = New-Object System.Net.Sockets.UdpClient
\$UDPClient.Connect(([System.Net.IPAddress]::Broadcast), 9)
[void]UDPClient.Send(packet, \(packet.Length)\)UDPClient.Close()
return \$true
}
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " 自动化流水线 [前置唤醒]: 资产库自适应无脑唤醒 " -ForegroundColor Cyan
Write-Host "==================================================" -ForegroundColor Cyan
# --- 环节 1: 资产库检索并群发封包 ---
\$WakeCount = 0
foreach (Computer in ComputerList) {
Computer = Computer.Trim()
Record = Database | Where-Object { \(_.ComputerName -eq\)Computer } | Select-Object -First 1
if (Record -and Record.MAC) {
Mac = Record.MAC
SendStatus = Send-WOLPacket -MacAddress Mac
if (\$SendStatus) {
Write-Host "已通过资产库匹配并唤醒: [\(Computer] -> (\)Mac)" -ForegroundColor Green
\$WakeCount++
}
} else {
Write-Host "无法唤醒 [\$Computer]: 本地资产库中未找到其 MAC 记录,请先在开机时运行采集脚本!" -ForegroundColor Red
}
}
Write-Host "唤醒封包已全部轰出!" -ForegroundColor Green
Write-Host "--------------------------------------------------"
Write-Host "正在等待 40 秒,留给目标计算机主板网络和系统启动..." -ForegroundColor Yellow
for (\$seconds = 40; seconds -gt 0; seconds--) {
Write-Progress -Activity "等待目标机唤醒上线中..." -Status "seconds 秒后自动开始进行 Ping 与防火墙扫描..." -PercentComplete ((seconds/40)*100)
Start-Sleep -Seconds 1
}
Write-Progress -Activity "等待目标机唤醒上线中..." -Completed
# --- 环节 2: 自动扫描,生成流水线 list2.txt ---
Write-Host "正在扫描哪些机器已成功苏醒并解封防火墙(正在生成 list2.txt)..." -ForegroundColor Yellow
if (Test-Path $List2Path) { Remove-Item $List2Path -Force }
$ReadyCount = 0
foreach ($Computer in $ComputerList) {
$Computer = $Computer.Trim()
$PingStatus = Test-Connection -ComputerName $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue
if ($PingStatus) {
$PortStatus = Test-NetConnection -ComputerName $Computer -Port 445 -WarningAction SilentlyContinue
if ($PortStatus.TcpTestSucceeded) {
Add-Content -Path $List2Path -Value $Computer
Write-Host " [$Computer] 已上线且防火墙就绪!已加入 list2.txt" -ForegroundColor Green
$ReadyCount++
} else {
Write-Host " [$Computer] 已上线,但 445 端口被防火墙拦截!无法加入 list2.txt" -ForegroundColor Yellow
}
} else {
Write-Host " [$Computer] 依旧处于睡眠或关机状态。" -ForegroundColor Red
}
}
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " 唤醒与优质名单筛选完成!共计 [ \$ReadyCount ] 台可直接升级。" -ForegroundColor Green
Write-Host " 下一步:请双击 1_Auto_Upgrade.bat 启动流水线升级。" -ForegroundColor Yellow
Write-Host "==================================================" -ForegroundColor Cyan
Read-Host "按任意键退出..."
更新指令下发后如何才能查看更新进度是否更新完成?
由于我们之前对 local_upgrade_final.bat 脚本进行了极高级的企业级优化(加入了 /noreboot 参数,且去除了 /quiet 模式下可能产生的硬弹窗限制),Windows 安装程序在完成第一阶段的所有文件解压、配置迁移和注入后,会安全挂起,绝对不会自动重启。
在控制端计算机上,我们可以通过远程读取目标机上的关键安装日志,来 100% 确认它是否已经“更新准备就绪,正在静默等待手动重启”。
以下是为你编写的控制端专用实时进度与完工状态检查脚本:
控制端状态雷达:2_Check_Progress.ps1
请在你的控制端计算机上,将以下代码保存为 2_Check_Progress.ps1(PowerShell 脚本)。它可以远程读取你在 list2.txt 中下发的机器,并精准判定其当前卡在哪个升级阶段:
# ==================================================
# 保存为: 2_Check_Progress.ps1 (右键使用 PowerShell 运行)
# 作用: 批量远程检查目标机的 Win11 静默更新真实完成进度与状态
# ==================================================
clear
ScriptDir = Split-Path -Parent MyInvocation.MyCommand.Definition
Set-Location \$ScriptDir
List2Path = "ScriptDir\list2.txt"
if (!(Test-Path \$List2Path)) {
Write-Error "找不到就绪列表 list2.txt,请确保之前运行过部署脚本!"
Read-Host "按回车退出..."; exit
}
\$ComputerList = Get-Content List2Path | Where-Object _.Trim() -ne "" }
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " Windows 11 局域网升级流水线进度与完工检查器 " -ForegroundColor Cyan
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host "提示: 当状态显示 [🎉 准备就绪] 时,即可安全下发手动重启命令。`n"
[string]::Format("{0,-15} | {1,-10} | {2,-15} | {3,-10}", "计算机名/IP", "进程状态", "已缓存文件体积", "当前升级所处状态") | Write-Host -ForegroundColor White
Write-Host ("-" * 75)
foreach ($Computer in $ComputerList) {
$Computer = $Computer.Trim()
# 1. 检查网络连通性
$Ping = Test-Connection -ComputerName $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue
if (!$Ping) {
[string]::Format("{0,-15} | {1,-10} | {2,-15} | {3,-10}", $Computer, "❌ 离线", " -- ", "机器断网或已自行重启") | Write-Host -ForegroundColor Red
continue
}
# 2. 检查后台核心进程是否在运行
$Process = Get-CimInstance -ComputerName $Computer -ClassName Win32_Process -ErrorAction SilentlyContinue |
Where-Object { $_.Name -eq "setup.exe" -or $_.Name -eq "setupprep.exe" }
$ProcStatus = if ($Process) { "⚡ 奔跑中" } else { "💤 已退出" }
# 3. 统计缓存文件夹体积
$Size = Get-ChildItem "\\$Computer\c$\`\$WINDOWS.~BT" -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum | Select-Object -ExpandProperty Sum
\$SizeGB = if (\(Size) { [math]::Round(\)Size / 1GB, 2) } else { 0 }
# 4. 终极状态判定:读取微软官方莫扎特日志 (setupact.log) 的最后几行
\(LogPath = "\$Computer\c\)\`$WINDOWS.~BT\Sources\Panther\setupact.log"
$StatusDescription = "正在启动初始化检查..."
$FinalColor = "Yellow"
if (Test-Path $LogPath) {
try {
# 远程读取最后 30 行日志以捕获关键结语
$LogTail = Get-Content -Path $LogPath -Tail 30 -ErrorAction SilentlyContinue
if ($LogTail -match "Requesting reboot" -or $LogTail -match "Setup has completed the pre-installation phase" -or ($SizeGB -gt 4.5 -and !$Process)) {
$StatusDescription = "🎉 准备就绪(等待重启)"
$FinalColor = "Green"
} elseif ($LogTail -match "FatalError" -or $LogTail -match "failed") {
$StatusDescription = "❌ 升级遇到硬错误卡死"
$FinalColor = "Red"
} elseif ($SizeGB -gt 0) {
$StatusDescription = "⏳ 正在疯狂解压与复制文件..."
$FinalColor = "Cyan"
}
} catch {
$StatusDescription = "无法读取日志,但正在复制..."
}
} else {
if ($Process) {
$StatusDescription = "安装启动中,正在创建临时目录..."
} else {
$StatusDescription = "❌ 脚本已被终止或未真正成功拉起"
$FinalColor = "Red"
}
}
# 打印单台结果
[string]::Format("{0,-15} | {1,-10} | {2,-15} | {3,-10}", $Computer, $ProcStatus, "$SizeGB GB", $StatusDescription) | Write-Host -ForegroundColor $FinalColor
}
Write-Host "`n==================================================" -ForegroundColor Cyan
Read-Host "按任意键退出..."
📊 如何通过屏幕上的三维指标精准判断?
当你运行此雷达脚本后,你会清晰看到三个数据维度的交叉反馈:
| 进程状态 | 已缓存文件体积 | 屏幕显示状态 | 🛠️ 运维动作 / 实际进度 |
|---|---|---|---|
| ⚡ 奔跑中 | 从几百 MB 持续飙升至 4GB~8GB | ⏳ 正在疯狂解压与复制文件... | 安装程序正在全速运转,用户正在平稳办公,无需干预,继续等待。 |
| 💤 已退出 | 稳定在 5GB~10GB 不再增长 | 🎉 准备就绪(等待重启) | 大功告成! 意味着第一阶段(静默灌入)已 100% 完工。进程已功成身退并安全退出,目前正处于完全安全状态,在等待你下发重启命令。 |
| 💤 已退出 | 0 GB 或几 MB | ❌ 脚本已被终止或未真正成功拉起 | 说明下发失败,或目标机环境存在致命硬件或系统报错,可在 list3.txt 中排查。 |
🏁 下一步:如何批量一键收尾?
当这批执行机在屏幕上陆续泛起 🟢 🎉 准备就绪(等待重启) 的绿色状态时,这说明你可以自由选择在中午午休、或者半夜下班后,统一给这批机器发送一条“定时/立即重启”指令。
机器一旦触发重启,由于所有补丁和无人值守配置在刚才已经百分之百注入到了 C 盘,它会自动进入蓝色的 Win11 进度跑圈,大约 15 分钟后自动跨入 Win11 桌面,且原有的个人软件、配置完好损耗!















