适用于 PowerShell 7.0 及以上版本,需要 winget CLI
背景介绍 在企业 IT 运维中,软件安装和更新是一项高频且重复的工作。新员工入职时需要配置开发环境,安全合规要求定期更新终端上的软件版本,这些都给运维团队带来了巨大的工作量。传统的手动安装方式不仅效率低下,还容易出现版本不一致、配置遗漏等问题。
WinGet(Windows Package Manager)是微软官方推出的命令行包管理工具,从 Windows 10 1709 版本开始内置。它提供了类似 Linux 下 apt 或 yum 的软件管理体验,支持搜索、安装、更新和卸载应用程序。结合 PowerShell 的脚本能力,我们可以将 WinGet 的操作封装为可复用的自动化流程。
本文将通过三个实际场景,演示如何使用 PowerShell 调用 WinGet 实现软件搜索安装、批量部署以及自动化更新,帮助运维人员构建完整的 Windows 软件生命周期管理体系。
WinGet 基础操作 WinGet 提供了丰富的子命令来管理软件包。下面的脚本展示了最常用的三个操作:搜索软件包、安装软件以及列出已安装的软件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 function Find-WinGetPackage { param ( [Parameter (Mandatory )] [string ]$Keyword ) $result = winget search $Keyword --accept-source-agreements 2 >&1 $result } function Install-WinGetPackage { param ( [Parameter (Mandatory )] [string ]$PackageId , [switch ]$Silent ) $args = @ ( 'install' '--id' , $PackageId '--accept-source-agreements' '--accept-package-agreements' ) if ($Silent ) { $args += '--silent' } Write-Host "正在安装: $PackageId " -ForegroundColor Cyan winget @args 2 >&1 } function Get-WinGetInstalled { $output = winget list --accept-source-agreements 2 >&1 $output } Find-WinGetPackage -Keyword "Visual Studio Code" Install-WinGetPackage -PackageId 'Microsoft.VisualStudioCode' -Silent Get-WinGetInstalled
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 名称 ID 版本 来源 --------------------------------------------------------------------------- Visual Studio Code Microsoft.VisualStudioCode 1.96.2 winget Visual Studio Code Insiders Microsoft.VisualStudioCode.Insiders 1.97.0 winget 正在安装: Microsoft.VisualStudioCode 已成功完成安装 名称 ID 版本 --------------------------------------------------------------------------- Visual Studio Code Microsoft.VisualStudioCode 1.96.2 Git for Windows Git.Git 2.47.1 PowerShell 7 Microsoft.PowerShell 7.4.6
通过上面的函数封装,我们可以将 WinGet 的命令行操作转化为结构化的 PowerShell 函数,便于后续在脚本中组合调用。--accept-source-agreements 和 --accept-package-agreements 参数可以跳过交互式确认,实现无人值守安装。
批量安装与软件清单管理 在企业环境中,我们通常需要根据角色或团队批量安装一组软件。下面的脚本展示了如何通过 JSON 清单文件管理软件列表,并实现一键批量部署。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 $softwareList = @ ( @ { Id = 'Microsoft.VisualStudioCode' Name = 'Visual Studio Code' Category = 'Editor' } @ { Id = 'Git.Git' Name = 'Git for Windows' Category = 'VCS' } @ { Id = 'Microsoft.PowerShell' Name = 'PowerShell 7' Category = 'Runtime' } @ { Id = 'OpenJS.NodeJS.LTS' Name = 'Node.js LTS' Category = 'Runtime' } @ { Id = 'Docker.DockerDesktop' Name = 'Docker Desktop' Category = 'Container' } ) | ConvertTo-Json -Depth 3 $listPath = Join-Path $HOME 'winget-software-list.json' $softwareList | Out-File -FilePath $listPath -Encoding utf8Write-Host "软件清单已保存到: $listPath " -ForegroundColor Greenfunction Install-WinGetFromList { param ( [Parameter (Mandatory )] [string ]$ListFile , [string []]$Categories ) $packages = Get-Content $ListFile -Raw | ConvertFrom-Json if ($Categories ) { $packages = $packages | Where-Object { $_ .Category -in $Categories } } $total = $packages .Count $success = 0 $failed = 0 foreach ($pkg in $packages ) { Write-Host "`n[$ ($success + $failed + 1)/$total ] 安装: $ ($pkg .Name)" -ForegroundColor Cyan $output = winget install --id $pkg .Id ` --accept-source-agreements ` --accept-package-agreements ` --silent 2 >&1 $lastLine = ($output | Select-Object -Last 1 ).ToString() if ($lastLine -match '成功|successfully|已安装' ) { Write-Host " -> 成功" -ForegroundColor Green $success ++ } else { Write-Host " -> 失败: $lastLine " -ForegroundColor Red $failed ++ } } Write-Host "`n===== 安装汇总 =====" -ForegroundColor Yellow Write-Host "总计: $total | 成功: $success | 失败: $failed " } Install-WinGetFromList -ListFile $listPath -Categories 'Runtime' , 'VCS'
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 软件清单已保存到: C:\Users\admin\winget-software-list.json [1/3] 安装: Git for Windows -> 成功 [2/3] 安装: PowerShell 7 -> 成功 [3/3] 安装: Node.js LTS -> 成功 ===== 安装汇总 ===== 总计: 3 | 成功: 3 | 失败: 0
使用 JSON 清单文件管理软件列表的好处是:不同团队可以维护各自的清单文件,新员工入职时只需指定对应的清单即可完成环境搭建。同时,清单文件可以纳入 Git 版本管理,便于审计和追溯软件配置变更。
软件更新检查与自动化更新 保持软件版本最新是安全运维的基本要求。下面的脚本实现了自动检测可更新的软件,并在确认后执行批量更新,同时支持将更新任务注册为 Windows 计划任务实现定时自动执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 function Get-WinGetUpdate { $output = winget upgrade --accept-source-agreements 2 >&1 $lines = $output -split "`n" $updates = @ () $dataStarted = $false foreach ($line in $lines ) { if ($line -match '^[-\s]+$' ) { $dataStarted = $true continue } if ($dataStarted -and $line .Trim() -and $line -notmatch '^\s*$' ) { $updates += $line .Trim() } } return $updates } function Start-WinGetUpdate { param ( [string ]$PackageId , [switch ]$All , [switch ]$WhatIf ) if ($All ) { Write-Host "检查所有可更新的软件..." -ForegroundColor Cyan $updates = Get-WinGetUpdate if ($updates .Count -eq 0 ) { Write-Host "所有软件均为最新版本" -ForegroundColor Green return } Write-Host "发现 $ ($updates .Count) 个可更新的软件:`n" $updates | ForEach-Object { Write-Host " - $_ " } if ($WhatIf ) { Write-Host "`n[WhatIf 模式] 跳过实际更新" -ForegroundColor Yellow return } $confirm = Read-Host "`n确认更新全部? (Y/N)" if ($confirm -eq 'Y' ) { winget upgrade --all ` --accept-source-agreements ` --accept-package-agreements 2 >&1 } } elseif ($PackageId ) { if ($WhatIf ) { Write-Host "[WhatIf 模式] 将更新: $PackageId " -ForegroundColor Yellow return } winget upgrade --id $PackageId ` --accept-source-agreements ` --accept-package-agreements 2 >&1 } } function Register-WinGetUpdateTask { param ( [string ]$Time = '03:00' ) $scriptPath = Join-Path $HOME 'winget-auto-update.ps1' $scriptContent = @" `$logDir = Join-Path `$HOME 'WinGetUpdateLogs' if (-not (Test-Path `$logDir)) { New-Item -Path `$logDir -ItemType Directory | Out-Null } `$logFile = Join-Path `$logDir "update-$ (Get-Date -Format 'yyyy-MM-dd').log" "[$ ((Get-Date))] 开始检查更新" | Out-File `$logFile -Append `$output = winget upgrade --all --accept-source-agreements --accept-package-agreements --silent 2>&1 `$output | Out-File `$logFile -Append "[$ ((Get-Date))] 更新完成" | Out-File `$logFile -Append "@ Set-Content -Path $scriptPath -Value $scriptContent -Encoding utf8 $action = New-ScheduledTaskAction -Execute 'pwsh.exe' ` -Argument "-NoProfile -File `"$scriptPath `"" $trigger = New-ScheduledTaskTrigger -Daily -At $Time $settings = New-ScheduledTaskSettingsSet ` -AllowStartIfOnBatteries ` -DontStopIfGoingOnBatteries Register-ScheduledTask ` -TaskName 'WinGet-AutoUpdate' ` -Action $action ` -Trigger $trigger ` -Settings $settings ` -Force Write-Host "已注册每日更新任务,执行时间: $Time " -ForegroundColor Green } Start-WinGetUpdate -All -WhatIf
执行结果示例:
1 2 3 4 5 6 7 8 检查所有可更新的软件... 发现 3 个可更新的软件 : - Git.Git 2.43.0 2.47.1 winget - Microsoft.VisualStudioCode 1.95.0 1.96.2 winget - Docker.DockerDesktop 4.34.0 4.35.1 winget [WhatIf 模式] 跳过实际更新
将更新检查注册为 Windows 计划任务后,系统会在每天凌晨自动检查并安装更新,同时将操作日志写入文件。运维人员可以通过查看日志目录下的文件,快速了解每台机器的软件更新情况,确保安全补丁及时到位。
注意事项
WinGet 版本要求 :建议使用 WinGet v1.6 或更高版本,旧版本可能不支持 --accept-package-agreements 等参数。可以通过 winget --version 查看当前版本,通过 Microsoft Store 自动更新 WinGet。
管理员权限 :部分软件的安装需要管理员权限。在脚本中可以使用 #requires -RunAsAdministrator 声明,或者以管理员身份运行 PowerShell 来执行安装操作,否则可能会遇到权限不足的错误。
网络环境 :在企业代理环境下,WinGet 可能需要额外配置才能正常访问软件源。可以通过 winget settings 命令打开配置文件,设置代理和网络相关参数,或者配置企业内部源(如 Azure Artifacts)作为替代。
安装失败处理 :某些软件不支持静默安装或存在安装依赖冲突。建议在批量部署前先在测试环境中验证清单文件,对于失败的安装记录错误日志并单独处理,避免一个失败阻塞整个部署流程。
计划任务兼容性 :Register-ScheduledTask 需要 Windows 8.1 及以上系统。在 Windows Server 环境中,需要确认 Task Scheduler 服务正常运行,且执行账户具有安装软件的权限。对于不支持计划任务的场景,可以考虑使用 Group Policy 的启动脚本替代。
PATH 环境变量刷新 :WinGet 安装完软件后可能修改了系统 PATH,但当前 PowerShell 会话不会自动感知变更。部署完成后需要重启 PowerShell 或手动刷新环境变量:$env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'User'),否则后续命令可能找不到新安装的可执行文件。