上周末帮家里一位经常重装系统的朋友做电脑交付,装完系统后的第一件事,不是去下载各种安装包,而是打开终端敲了几条 winget 命令。看着他一脸不解的表情,我想起自己刚接触包管理时的样子。WinGet(Windows Package Manager)是微软官方推出的命令行包管理工具,可以把“打开浏览器搜软件、找官网、下载安装包、点安装、同意捆绑协议”这一整套流程,压缩成一行命令。这篇文章我会把 WinGet 的下载方式、安装细节、初始化设置,以及从搜索到批量升级的完整使用链路全部整理出来,适合刚接触包管理的开发者,也适合不想再被各种软件捆绑安装折腾的普通用户。
1. 先从三个问题搞清楚 WinGet 到底解决了什么
1.1 没有包管理器之前,Windows 用户的日常有多痛
回想一下没有包管理软件的日子:装一个 Git,要去官网找下载入口,下载完双击 exe,一路 Next,中间还要小心别点到捆绑安装的垃圾软件。想更新一款软件,得重新打开官网,重新下载新版本,把旧的卸载掉再装上,配置可能就跟着丢了。更常见的情况是,很多软件自带“自动更新”,但这些更新往往伴随着弹窗、开机自启,甚至偷偷装上全家桶。
卸载更麻烦。控制面板里的“卸载程序”只是移除主体,桌面快捷方式、注册表残留、AppData 里的缓存文件散落一地。时间久了,C 盘空间越来越少,软件越用越乱,却说不清是哪个安装包惹的祸。
WinGet 解决的就是这个层面的问题。它把软件包抽象成一个个有唯一 ID 的“源条目”,搜索、安装、卸载、升级全部走同一条命令体系。装上它之后,配一台新电脑的软件环境,基本上就是端着茶杯看终端滚动。
1.2 用手机应用商店来理解 WinGet 的工作模式
你可以把 WinGet 理解成 Windows 生态里默认跑的“命令行版应用商店”。它背后连接的是一个软件仓库,里面收录了成千上万款常用软件的元数据,包括软件的唯一标识(Package ID)、版本号、安装包类型、下载地址、哈希值等。
当你执行winget install 某个ID时,WinGet 会去源仓库里找到对应的安装包信息,校验完整性之后,根据安装包的类型选择对应的安装策略。如果是 MSI 或 EXE 安装包,它会尝试用静默参数执行安装;如果是 MSIX 包,则直接通过部署接口部署。整个过程并不需要你在浏览器里亲自操作。
这个思路其实和吃外卖一模一样——你不需要知道饭馆的后厨在哪,只需要在外卖平台上下单,然后等骑手把饭送到门口。WinGet 就是这个“外卖平台”,软件源里的包就是“后厨的菜单”。
1.3 和微软商店比,WinGet 的优势在哪里
很多人会问:Windows 不是自带 Microsoft Store 吗?为什么还要用 WinGet?
两者分工不同,而且可以互补。Microsoft Store 面向的是普通消费者,收录的是经过商店认证的 UWP 应用和少量 Win32 应用,界面友好、安装干净,但覆盖面有限。WinGet 面向的是开发者、IT 管理员和喜欢折腾的终端用户,收录范围更广,很多传统桌面软件(比如开发工具、终端模拟器、压缩软件)在商店里可能找不到,但 WinGet 仓库里有。
更关键的是,WinGet 是可脚本化的。你能把安装软件这件事写进一个 PowerShell 脚本里,一键部署整个开发环境,这在企业批量装机和个人重装系统时都非常实用。商店给你的是一个图形界面,WinGet 给你的是一整套自动化入口。
2. 装之前先确认四件事,能省掉后面 80% 的排错时间
2.1 系统版本:WinGet 的硬门槛
WinGet 可以运行在 Windows 10 1809(17763)及更高版本,以及 Windows 11 全系。理论上只要系统达到这个门槛,就能装。
查看自己系统的具体版本,最快的方式是Win + R输入winver回车,弹窗里会显示版本号和内部版本号。如果你还在用更老的 Windows 7 或 Windows 8.1,建议放弃 WinGet,先把系统升级到至少 Windows 10 1809 再说。这不是势利,而是 WinGet 依赖现代 Windows 的打包部署基础组件,旧系统缺胳膊少腿,强装上去问题一大堆。
2.2 核心组件:App Installer 的存在与版本
WinGet 早就不是“一个安装包直接装完”的独立程序,它的本体其实藏在App Installer这个系统组件里。App Installer 是微软商店里的一个应用包,WinGet 的 CLI 入口就是它提供的。
所以判断系统有没有 WinGet,本质上是在判断 App Installer 的可用状态。我见过不少朋友把网上教程里的安装包下载下来,双击却发现部署失败,原因就是他们忽略了 App Installer 的版本太旧,或者干脆被精简掉了。
检查新版 App Installer 是否已存在的命令很简单:
winget --version如果终端返回了类似v1.7.xxxx的版本号,说明你的系统里已经有可用的 WinGet 了,而且版本越新,功能和修复的 bug 越多。如果提示“找不到命令”,说明系统里没有 App Installer,或者它的版本老到不兼容。
2.3 应用商店有没有被禁用
由于 WinGet 最方便的安装渠道是 Microsoft Store,安装前要确认商店没有被组策略或企业镜像禁用。尤其是在单位配发的电脑上,管理员经常把商店整个锁掉,这时候就算你下载了安装包,也可能因为缺少商店相关的依赖服务而装不上。
打开开始菜单,搜一下“Microsoft Store”。如果能正常打开、能浏览应用列表,就说明商店是活的。如果点击没反应,或者打开后一直转圈,优先去“设置 -> 应用 -> 可选功能”里看看商店相关服务有没有被关闭,再不行就用后面提到的 GitHub 离线部署方式绕过商店。
2.4 网络环境是否允许访问软件的官方下发端点
WinGet 默认的源是官方软件源,安装、更新源索引,都需要访问全球网络。在一些实验性网络、企业内网里,可能会遇到官方端点连接超时的情况。装之前最好用浏览器访问一下软件源所在站点的响应能力,如果网络不稳定,后面的每一步都会卡到怀疑人生。
不过别慌,这篇文章后面会讲到换源和离线安装的方案。总的来说,网络问题不是 WinGet 的硬伤,只是需要多一步配置。
3. 三种下载安装方式对比,总有一种能贴合你的网络条件
3.1 方式一:从 Microsoft Store 安装 App Installer(最省心)
这是绝大多数普通用户应该选的路径,操作难度最低,依赖关系也最少。
具体步骤:
- 打开 Microsoft Store。
- 右上角搜索框里输入“App Installer”。
- 找到微软官方发布的那一款(发布者显示为 Microsoft Corporation),点击获取或更新。
- 等待安装完成,打开终端验证:
winget --version能看到版本号就说明成功了。
我推荐普通用户优先走这条路,因为 Store 版本会自动跟随系统更新,不用担心 WinGet 自身版本落后。而且 Store 会顺带把 WinGet 依赖的框架组件一起处理好,省去手动装依赖的麻烦。
3.2 方式二:从官方发布仓库直接下载安装包(适合能正常访问海外网络的情况)
如果网络环境能稳定访问海外资源,可以直接从微软官方维护的发布仓库下载最新版本的安装包。这种方式不需要商店,前提是网络可靠。
操作过程大致如下:
进入到发布仓库的 Releases 页面,在最新版本的 Assets 区域找到后缀为.appxbundle的文件(这是 WinGet 主安装包),同时下载同版本下的.cer证书文件。
下载之后,先安装证书:
Import-Certificate -FilePath .\你的证书文件.cer -CertStoreLocation Cert:\LocalMachine\TrustedPeople接着部署主包:
Add-AppxPackage -Path .\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle -ForceApplicationShutdown安装完成后同样用winget --version验证。
在实际操作中,Add-AppxPackage经常因为依赖缺失而失败。WinGet 主包依赖 Visual C++ 运行库框架包(VCLibs)等组件,遇到提示“缺少依赖”时,先把这些框架包也一并下载,按“框架包在前、主包在后”的顺序依次执行Add-AppxPackage。
3.3 方式三:离线部署包方案(适合内网、断网环境)
企业内网、实验机房这类不允许联网的环境,也可以用离线方式部署 WinGet。
思路很简单:准备一台能够联网的机器,把 WinGet 主安装包以及所有依赖框架包全部下载到本地,复制到目标机器上之后,按照依赖倒序逐个部署。
我实际跑过的离线部署顺序大致是:
- 安装 VC++ 运行库框架包(VCLibs)
- 安装 .NET 或 WinUI 相关框架(如果目标系统缺失)
- 最后安装 App Installer 主包
这个方案不需要网络,但在批量部署前,最好先在一台干净的虚拟机里跑一遍,确认你收集的依赖清单是完整的,否则几十上百台机器一起报缺依赖,现场很狼狈。
3.4 三种方式怎么选
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 普通个人电脑,商店可用 | Store 安装 | 零门槛,自动更新 |
| 网络条件能访问海外资源 | Release 文件安装 | 版本可控,最新特性第一时间体验 |
| 企业内网批量部署 | 离线包方案 | 不依赖外网,可控性最强 |
如果你拿不准自己属于哪种情况,我的建议是无脑先试 Store,不行再上 Release,实在不行再离线部署。这三条路不存在谁比谁更高级,只是适应不同环境。
4. 装好别急着搜软件,先把两处基础配置处理完
4.1 把 WinGet 自身更新到最新版本
刚装完的 WinGet 不一定是全通道最新版,尤其是 Store 分发存在延迟。旧版本在搜索、安装新软件时会遇到兼容性问题。
更新 WinGet 自身,其实就是把 App Installer 这个包升级到最新版本:
winget upgrade --id Microsoft.DesktopAppInstaller -e很长一段时间里,WinGet 升级自己会出现“升级源为自身导致冲突”这种灵异现象。如果上面这条命令报错,先手动更新一下源列表:
winget source update然后再试一次升级命令。实在不行,用前面的方式二直接手动装新版安装包,覆盖升级。
4.2 给网络不稳的用户配置一个可用的源
WinGet 默认只启用一个官方源,名字就叫winget。在境外网络环境下,这个源的响应速度极快;但如果是境内普通宽带,访问官方源经常超时,症状就是winget search半天没反应,或者直接报“无法从源中检索信息”。
查看当前启用的源:
winget source list如果你所在环境访问官方源不顺畅,可以用国内高校维护的开源镜像站做替代或补充。添加一个镜像源的方法:
winget source add --name mymirror --type REST --arg https://mirror.example.edu.cn/winget注意,--type REST表示这是一个 REST 协议源,--arg后面跟的是镜像地址。加完之后用winget source list确认新源已经在列表里,以后搜索时默认会同时查官方源和新源,哪个响应快用哪个。
有些同学喜欢把官方源直接 remove 掉,只留镜像源。我个人不建议这么做,因为镜像源的数据同步存在延迟,某些刚发布的新版本可能在镜像上查询不到。保留官方源作为兜底,配合网络加速手段使用,才是稳定策略。
4.3 合理开启实验性功能
WinGet 有些高级能力需要手动在设置文件里开启实验性开关。设置文件位于用户目录下的:
%LocalAppData%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json没有这个文件就手动创建一个。一个常见的配置示例:
{ "experimentalFeatures": { "experimentalMSIX": true, "uninstall": true } }experimentalMSIX用来支持部分 MSIX 包的精细操作,uninstall开关则是一些卸载增强命令的前置条件。新版本中不少功能已默认启用,如果你发现某个命令提示“需要实验性功能”,再回来打开对应开关即可。
4.4 设置默认接受源与包协议
WinGet 首次安装软件时,会弹出一堆协议确认提示,在交互式终端里一键确认并不麻烦,但在脚本里就是天大的障碍。
解决方案是统一加两个参数:
--accept-package-agreements --accept-source-agreements前者表示接受包本身的许可协议,后者表示接受源仓库的服务条款。写脚本时只要把这两个参数加上,就不会在协议确认处卡住。我自己习惯把它们写进一个自定义的安装函数里,每次调用都自动带上,省心得很。
5. 常用命令清单:从搜索到批量卸载一次说清
5.1 搜索软件:winget search
想找一个软件,但不知道它的确切 ID 时,用搜索:
winget search vscode返回结果里,Id列是最关键的信息,它决定了后面所有操作的准确性。搜索结果通常包含多个相似项,需要仔细区分发布者。比如搜git很可能出来好几个同名工具,有的是 Git 官方客户端,有的是第三方 GUI 外壳,选错了等于装了个别的软件。
精确匹配搜索的写法:
winget search --id Git.Git -e-e表示精确匹配,输出只有一条记录的时候,基本可以确认这就是目标软件。
5.2 查看软件详情:winget show
搜索到 ID 之后,在安装之前最好先看下详细信息:
winget show Git.Git输出会列出软件的版本、发布者、安装包类型、许可证、源代码地址等信息。这些信息能帮你判断这个包是不是你想要的版本渠道。比如有些软件同时有稳定版和预览版两个 ID,show一眼就能看清楚差异。
加--versions参数还可以列出该软件在源里支持的所有历史版本:
winget show Git.Git --versions这在需要固定特定版本时非常有用,比如公司内部代码仓库要求 Git 2.40.x 或更高版本,你就可以精确指定。
5.3 安装软件:winget install
安装是 WinGet 的核心功能,基础命令:
winget install --id Git.Git -e实际安装时,我通常会组合几个参数:
winget install --id Git.Git -e --source winget --scope user --silent--source winget:强制指定从官方源安装,避免歧义。--scope user:按用户级安装,对没有管理员权限的账户很友好。--silent:静默安装,不弹安装向导。
默认情况下,很多大型软件(比如 Visual Studio、Office)安装时会自动采用静默参数,这一步对普通用户是透明的。但要注意,并不是所有安装包都支持静默,少数软件强制需要交互式安装界面,这种时候把--silent去掉即可。
安装完成后,我习惯顺手验证一下:
git --version如果终端能正常响应版本号,说明软件真正可用了。有些软件安装后需要重启才会加入 PATH,遇到命令找不到别急着卸载,先重新打开一个终端窗口再说。
5.4 查看已安装软件:winget list
每次装机装到一半,总会想知道某软件到底装了没有,手速快一点直接用:
winget list --id Git.Git -e输出正常说明已安装,输出为空说明未安装。如果想盘点整台机器上所有由 WinGet 管理的软件:
winget list这个命令会列出系统里大部分已知应用,但请注意,有些软件是自己写注册表项的,WinGet 识别不到,列表里看不到不代表没装。
5.5 卸载软件:winget uninstall
卸载命令:
winget uninstall --id Git.Git -e卸载操作比安装更需要小心。有些软件卸载时会询问是否保留用户配置,WinGet 的静默卸载默认会走软件的默认卸载策略,配置可能会被一并清掉。如果你在意配置文件,先手动打开软件的设置界面备份,再执行卸载。
少数顽固软件卸载不干净,WinGet 卸载完成后,安装目录和注册表项还会残留。遇到这种情况,先用winget list确认主程序确实没了,再手工清理空目录,一般问题不大。
5.6 升级软件:winget upgrade
查看所有需要更新的软件:
winget upgrade没错,upgrade命令不加任何参数会列出所有可更新的包。全部更新:
winget upgrade --all如果某几个软件你不想更新(比如大版本升级后可能出现兼容性问题),新版本里支持“锁定”机制:
winget pin add --id Git.Git -e锁定之后upgrade列表里就不会再出现这个软件。解除锁定:
winget pin remove --id Git.Git -e我一般会把整个办公室常用的几个大型 IDE 全部 pin 住,因为这类软件的自动大版本升级,经常把同事辛苦调好的插件配置冲掉。
6. 进阶玩法:批量安装开发环境与配置迁移
6.1 一个脚本解决新机器的初始化问题
WinGet 最大的价值在于“可编排”。我写过一个很简陋但非常实用的批处理脚本,每次拿到新电脑或者重装完系统,先在 PowerShell 里跑一遍,开发环境就齐了一大半。
$apps = @( "Git.Git", "Microsoft.PowerShell", "Microsoft.VisualStudioCode", "7zip.7zip", "Google.Chrome", "Notepad++.Notepad++", "Python.Python.3.12", "OpenJS.NodeJS.LTS" ) foreach ($app in $apps) { Write-Host "正在安装: $app" -ForegroundColor Green winget install --id $app -e --accept-package-agreements --accept-source-agreements }这个脚本做的事情很简单:定义了一个数组,里面是所有要装软件的 ID,然后循环执行winget install。关键是协议参数已经默认接受,全程不需要人工干预。
配合笔者的习惯,我会在这个脚本前面加一段检查 WinGet 是否存在的逻辑:
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Write-Host "未检测到 WinGet,请先安装" -ForegroundColor Red exit 1 }这样即使在一台全新的裸机上执行脚本,也能第一时间给出明确提示,而不是抛出一大串红色的报错堆栈。
6.2 用导出导入命令,把软件清单从一台机器搬到另一台
WinGet 从比较早的版本开始支持export和import,可以把当前机器上通过 WinGet 安装的所有软件清单导出成一个 JSON 文件,再到新机器上导入安装。
winget export -o apps.json导出的 JSON 里记录了软件 ID、来源、版本等关键信息。到了新机器上,执行:
winget import -i apps.json --accept-package-agreements --accept-source-agreementsWinGet 会逐条解析 JSON,把缺失的软件安装上,已经存在的软件会自动跳过。
实际使用时有两个坑要提前知道。第一,导出清单里如果包含 Store 来源的软件,导入时有时候会提示来源不可用,需要在命令后加--include-unknown之类的参数来处理;第二,版本信息如果锁定得过死,后续软件更新后清单导入会匹配不到,所以我一般只在同批次同型号的办公电脑之间做导入,比如一班 30 台机器,标准环境一次搞定。
6.3 在无交互环境下的自动化保持良好的日志习惯
脚本化运行最怕的就是中间失败却没有任何记录。我强烈建议在跑批量安装时,把输出重定向到日志文件:
winget install --id Git.Git -e --accept-package-agreements --accept-source-agreements > install.log 2>&1这样即便半夜跑批,第二天早上打开日志就知道哪一步卡了、哪个包失败了。排查时先看日志尾部,再针对性重跑失败的那一个软件,效率会高很多。
7. 排错实战:我踩过的高频问题与完整排查链路
7.1 症状:search 和 install 一直转圈,最后报网络错误
这是我见过最多的问题。第一次用 WinGet,执行winget search,终端卡住不动,过一会儿提示无法从源中检索。
排查路径我一般按顺序走:
# 第一步:确认源列表状态 winget source list # 第二步:主动触发一次源更新 winget source update # 第三步:换个源搜索,验证是不是官方源网络问题 winget search vscode --source mymirror如果换了镜像源能秒搜出来,问题基本锁定在官方源的网络不可达。处理方案就是上面说的:保留官方源,添加一个响应快的镜像源,日常使用 WinGet 时让它自己去选源即可。
7.2 症状:安装成功但软件无法启动
这类问题的典型套路是:终端显示“已成功安装”,桌面上也能看到图标,但点击后一直没反应,或者立刻闪退。
原因大概率出在静默安装参数上。某些软件本身就抵触静默安装,WinGet 调用它的无人值守模式失败了,但返回码是 0,界面层就误判为成功。面对这种软件,把命令行里的静默参数去掉,改用交互式安装:
winget install --id 某个ID -e --interactive有些兼容性很差的软件,WinGet 用系统自带的静默参数完全无法驱动,这时我会直接放弃在 WinGet 层面解决,改成从官网下载安装包手动装,这是最务实的做法。包管理工具也不是万能钥匙。
7.3 症状:部署安装包时报“当前系统未安装应用商店”
很多精简版 Windows 镜像或企业定制镜像,把 Store 组件整个砍掉了。但 WinGet 的核心组件 App Installer 依赖的是商店基础设施,哪怕你是用 Release 部署包手动安装,也摆脱不了这个依赖。
我的处理思路:
- 先试手动部署 Release 包,如果在安装依赖框架包时提示缺少商店组件,就说明系统镜像的商店被彻底削掉了。
- 检查系统设置里是否有“应用商店”可选功能,有的话先把商店组件加回去。
- 如果公司政策禁止恢复商店,那就彻底放弃 WinGet 这条路,改用其他软件分发工具或脚本去解决同一问题。
这个场景说明了一件事:WinGet 虽然被包装成了一个独立工具,但血统上它仍然是商店家族的一员,绕不开商店基础组件的时候就别硬绕。
7.4 症状:卸载某软件时报“目录不是空文件夹”或者“另一个程序正在使用此文件”
卸载 VSCode、Docker Desktop 这类深度依赖用户目录的软件时,经常遇到残留文件锁死主程序定位表项。WinGet 的卸载只是调用了卸载程序的主体逻辑,而子进程退出前可能还没来得及释放全部文件句柄。
我的解决办法是分两步走:
- 关闭包括系统托盘在内的所有相关进程,然后再执行一次
winget uninstall --id 某个ID -e。 - 如果还是失败,手动删除残留目录(一般是
%LocalAppData%\Programs\...以及%AppData%\...下对应目录),然后重试卸载。
这里我要强调一个经验:很多时候“卸载不干净”的锅其实不在 WinGet,而是这些软件自身卸载器写得马虎。WinGet 只是把卸载命令转交给了卸载器,卸载器都不肯好好干活,工具层面再强大也没辙。
7.5 症状:突然提示 WinGet 自身版本过旧,命令不可用
WinGet 的很多高级命令会在背后请求源里的额外信息,如果本地版本的协议和源服务端不匹配,就会提示版本过旧或接口不支持。
解决办法还是那句话:先更新源列表,再升级自身:
winget source update winget upgrade --id Microsoft.DesktopAppInstaller -e如果升级时提示“正在使用中”导致文件锁住,关掉所有终端窗口,再开一个新终端重试覆盖升级。这类问题没有更高效的办法,只能保持 WinGet 新版本不断档。
8. 把 WinGet 当成生活方式之后的一点心得
用了一年多 WinGet,最大的体会是“别把它当成一个软件,把它当成你管理 Windows 软件环境的账本”。每次装了新软件,它会帮你记住;每次需要调整环境,它会帮你拆卸;每次大批量迁移环境,它还能导出一份清单给你带着走。
最后再分享一个小技巧:在 PowerShell 的$PROFILE文件里,把最常用的命令做成几个函数别名,比如win代表winget upgrade --all,ins代表winget install --id。这样日常使用连完整的命令都不用敲,效率直接上一个台阶。
WinGet 也有它的局限性,不是所有软件都能在仓库里找到,也不是每个软件的静默参数都那么配合。但作为 Windows 生态里免费的、官方出品的包管理工具,它已经是提升日常效率性价比最高的选择之一。希望这篇从下载到排错的完整梳理,能让你在使用 WinGet 的路上少走几步冤枉路。