powershell 批量卸载程序
<#
CUDA 11.8 组件一键卸载脚本
来自控制面板截图清单:
NVIDIA CUDA Development 11.8
NVIDIA CUDA Documentation 11.8
NVIDIA CUDA Nsight NVTX 11.8
NVIDIA CUDA Runtime 11.8
NVIDIA CUDA Visual Studio Integration 11.8
NVIDIA HD 音频驱动程序 1.3.39.16
NVIDIA Nsight Compute 2022.3.0
NVIDIA Nsight Systems 2022.4.2
NVIDIA Nsight Visual Studio Edition 2022.3.0.22245
NVIDIA PhysX 系统软件 9.21.0713
NVIDIA RTX Desktop Manager 203.20
NVIDIA Tools Extension SDK (NVTX) - 64 bit
NVIDIA USBC Driver 1.50.831.832
NVIDIA 图形驱动程序 522.06
#>
# 【设置】 $PreviewMode=$true 只预览,不卸载;改为 $false 执行静默卸载
$PreviewMode = $true
# 要卸载的软件名称数组(和控制面板名称匹配,支持模糊匹配)
$targetList = @(
"NVIDIA CUDA Development 11.8",
"NVIDIA CUDA Documentation 11.8",
"NVIDIA CUDA Nsight NVTX 11.8",
"NVIDIA CUDA Runtime 11.8",
"NVIDIA CUDA Visual Studio Integration 11.8",
"NVIDIA HD 音频驱动程序",
"NVIDIA Nsight Compute 2022.3.0",
"NVIDIA Nsight Systems 2022.4.2",
"NVIDIA Nsight Visual Studio Edition 2022.3.0",
"NVIDIA PhysX 系统软件",
"NVIDIA RTX Desktop Manager",
"NVIDIA Tools Extension SDK (NVTX)",
"NVIDIA USBC Driver",
"NVIDIA 图形驱动程序 522.06" # 不想删显卡驱动,请删除此行!
)
# 读取注册表获取已安装程序(32+64位)
$regPaths = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
$allApps = Get-ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName }
Write-Host "`n===== 检索匹配到的NVIDIA CUDA11.8组件 =====" -ForegroundColor Cyan
$toUninstall = @()
foreach ($name in $targetList) {
$matchApp = $allApps | Where-Object { $_.DisplayName -like "*$name*" }
if ($matchApp) {
foreach ($app in $matchApp) {
Write-Host "✅ $($app.DisplayName)"
$toUninstall += $app
}
}else{
Write-Host "❌ 未找到:$name" -ForegroundColor DarkGray
}
}
if($PreviewMode){
Write-Host "`n===== 当前是【预览模式】,不会卸载任何软件 =====" -ForegroundColor Yellow
Write-Host "修改脚本中 `$PreviewMode = `$false 再运行,才会开始静默卸载`n"
pause
exit
}
# 执行静默卸载
Write-Host "`n===== 开始静默卸载(会弹出少量确认窗口) =====" -ForegroundColor Red
foreach ($app in $toUninstall) {
$uninst = $app.UninstallString
if(-not $uninst){
Write-Warning "$($app.DisplayName) 没有找到卸载字符串,跳过"
continue
}
Write-Host "正在卸载:$($app.DisplayName)"
# 处理msi卸载
if ($uninst -match "^MsiExec\.exe\s+/I"){
$uninst = $uninst -replace "/I", "/X"
Start-Process msiexec.exe -ArgumentList "$($uninst -replace 'MsiExec.exe','') /qn" -Wait -NoNewWindow
}else{
Start-Process cmd -ArgumentList "/c $uninst /s /v /qn" -Wait -NoNewWindow
}
}
Write-Host "`n✅ 卸载任务全部执行完成!请重启电脑。`n" -ForegroundColor Green
Write-Host "【后续手动清理】:`n1. 删除环境变量 CUDA_PATH CUDA_PATH_V11_8`n2. 删除文件夹 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8`n"
pause
解读片段
代码片段
# 读取注册表获取已安装程序(32+64位)
$regPaths = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
$allApps = Get-ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName }
🔹第一部分:$regPaths = @(...)
语法
@( ... ) 是 PowerShell数组构造运算符,用来创建数组。
这里把两条注册表路径存进数组变量$regPaths。
两条注册表路径含义(Windows卸载注册表)
HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
64位程序的卸载注册表位置(64‑bit Windows)
HKLM = HKEY_LOCAL_MACHINE(本机计算机,机器级安装的软件)
子项Uninstall下面每一个子键代表一个安装的软件,保存卸载命令、显示名称、版本。
HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
Wow6432Node:专门存放32位软件在64位Windows上的注册表重定向位置。
在64位系统,32位应用看不到上面的原生Software,系统自动把它重定向进Wow6432Node。
✅ 想要完整列出本机所有软件,必须同时读这两处;只读其中一处会漏掉32位或者64位程序。
💡如果你是原生32位Windows,
Wow6432Node这个节点本身就不存在。
🔹第二行:$allApps = Get‑ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where‑Object { $_.DisplayName }
① Get‑ItemProperty -Path $regPaths
Get‑ItemProperty:读取注册表项的值(键值),不是读取项名称。-Path $regPaths:因为$regPaths是数组,这条命令会遍历数组里面两条注册表路径,把路径下面所有子键的属性全部取出来。-ErrorAction SilentlyContinue
错误行为策略:遇到报错直接静默跳过,不抛出红色异常。
用处:32位系统不存在Wow6432Node节点;访问的时候会报错,这个参数直接屏蔽该错误,脚本不会中断。
⚠️注意区分两个cmdlet:
Get‑Item:获取注册表项(文件夹)本身Get‑ItemProperty:获取注册表项里面保存的键值数据(DisplayName, UninstallString, DisplayVersion……)
Uninstall下面每一个子键里面就存放:DisplayName(控制面板显示的软件名字),UninstallString(卸载命令字符串),版本号。
② | Where‑Object { $_.DisplayName } 管道过滤
| 管道符号:把上一条命令输出的每一个注册表对象,依次传给后面Where‑Object过滤器。
$_:管道当前遍历到的对象(每一条软件注册表记录)。$_.DisplayName:读取该注册表记录的DisplayName属性(就是控制面板“程序和功能”里面显示的软件名称)。
👉过滤逻辑:只保留那些DisplayName不为空的记录。
为什么要过滤?
Uninstall注册表里面有大量隐藏、内部组件,很多子键没有DisplayName字段;这些项目不会在控制面板里显示出来。如果不过滤,返回结果里面会混杂一大堆无名内部条目。
Where‑Object { $_.DisplayName }等价于Where‑Object { $_.DisplayName -ne $null -and $_.DisplayName -ne "" }
PowerShell中在布尔上下文中,非空字符串判定为$true;null/空字符串判定$false直接被筛掉。
③ $allApps = (...)
把经过筛选之后的全部已安装软件对象集合,保存到变量$allApps。
后续脚本就可以循环$allApps,访问成员属性:
$app.DisplayName→ 软件显示名称$app.UninstallString→ 卸载命令字符串$app.DisplayVersion→ 版本号
📝整体完整执行逻辑梳理(流程)
- 先定义数组,保存两处卸载注册表路径(64位程序位置 + 32位重定向位置)
Get‑ItemProperty同时读取两处注册表下全部子键的属性;遇到不存在节点就静默忽略错误- 通过管道
Where‑Object过滤,丢弃没有DisplayName的无名注册表项 - 剩下就是和控制面板“程序和功能”基本一致的软件列表对象集合,存入$allApps
🧪简易测试,你可以直接在powershell管理员运行看效果:
$regPaths = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
$allApps = Get-ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName }
$allApps | Select‑Object DisplayName,UninstallString | Out‑GridView
Out‑GridView会弹出表格窗口可视化查看本机读取到的软件清单。
✨补充小坑说明
这种读取方式只能读取机器级安装(HKLM)的软件;当前用户安装(HKCU)的应用不会被读到。
用户级安装的程序保存在HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*,如果你的脚本需要读取用户安装软件,还需要额外加上这条路径。
NVIDIA CUDA toolkit全部是机器级安装,所以原脚本不需要HKCU路径就够用。
解读片段2
Where‑Object(PowerShell)
📌基本功能
Where‑Object 过滤器。接收管道过来的一组对象,按条件筛选;保留条件结果为 $true 的对象,丢掉 $false 的对象。
别名:where、?(问号简写)
类比SQL:
WHERE子句;相当于集合的筛选。
✅两种语法形式
形式1:脚本块语法(大括号,最常用,旧写法,脚本普遍用这个)
集合 | Where‑Object { 判断表达式 }
- 在
{ }脚本块内部:$_代表管道里当前正在遍历的那一个对象。
示例1(筛选名字长度大于5的进程)
Get‑Process | Where‑Object { $_.Name.Length -gt 5 }
$_就是每一个进程对象;$_.Name取进程名字。
👉 你前面CUDA脚本里这一句:
Where‑Object { $_.DisplayName }
脚本块里面只写 $_.DisplayName。
PowerShell会把这个属性的值转为布尔:
- 如果
DisplayName不为$null、不是空字符串 → 真值,保留该注册表条目 - 如果
DisplayName是空或者null → 假值,过滤直接丢弃
等价完整写法:
Where‑Object { $_.DisplayName -ne $null -and $_.DisplayName -ne '' }
形式2:简化语法(比较语法,PowerShell 3.0以后新增,不用大括号)
集合 | Where‑Object -Property 属性名 -Operator 比较值
# 别名语法
集合 | where Name -like "note*"
例子:
Get‑Process | Where‑Object -Property Name -like "note*"
🧩$_ 是什么(重点!)
$_是管道当前对象内置变量,只存在于ForEach‑Object {}、Where‑Object {}的脚本块里面。
每循环一次管道流,$_自动指向流经的那1个元素。
举个简单数组演示:
1,2,3,4,5 | Where‑Object { $_ -gt 3 }
#输出:4,5
这里$_就是数组里面每一个数字。
📖常用比较运算符(写筛选条件)
| 运算符 | 含义 |
|---|---|
‑eq |
等于 |
‑ne |
不等于 |
‑gt |
大于 |
‑lt |
小于 |
‑like |
通配符模糊匹配,*代表任意字符 |
‑match |
正则表达式匹配 |
多条件,用 -and / -or
#筛选名称含有NVIDIA并且版本大于11的软件
$allApps | Where‑Object { $_.DisplayName -like "*NVIDIA*" -and $_.DisplayVersion -ge "11" }
⚠️常见易错点
- ❌不要把
$_写到大括号外面!$_只在脚本块{ }内部有效。 Where‑Object不会修改原始数据源!只是输出筛选后的新对象流。
也就是说:
$result = $allApps | Where‑Object { $_.DisplayName‑like "*CUDA*" }
$allApps本身不变;筛选结果放到新变量$result。
- 简写别名
?(问号)
1..10 | ? { $_ %2 -eq 0 } #筛选偶数
🧪回到你原来那行完整语句再复盘
$allApps = Get‑ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where‑Object { $_.DisplayName }
管道前面输出一堆注册表项对象;交给Where‑Object过滤;只保留那些存在DisplayName(控制面板可见)的软件记录;最后赋值保存给$allApps。
为什么需要这个过滤:Windows卸载注册表里面有大量后台组件、补丁项,没有DisplayName,控制面板不显示;直接读取会拿到一堆无用条目,Where‑Object把它们筛掉。
解读片段3
这段PowerShell代码完整解析
Write-Host "`n===== 检索匹配到的NVIDIA CUDA11.8组件 =====" -ForegroundColor Cyan
$toUninstall = @()
foreach ($name in $targetList) {
$matchApp = $allApps | Where-Object { $_.DisplayName -like "*$name*" }
if ($matchApp) {
foreach ($app in $matchApp) {
Write-Host "✅ $($app.DisplayName)"
$toUninstall += $app
}
}else{
Write-Host "❌ 未找到:$name" -ForegroundColor DarkGray
}
}
前置上下文说明:
$targetList:事先定义好的待卸载软件名称字符串数组(CUDA各个组件名字)$allApps:前面从注册表读取得到的本机已安装软件对象集合(每个对象有DisplayName、UninstallString等属性)
1️⃣ 第一行:Write‑Host
Write-Host "`n===== 检索匹配到的NVIDIA CUDA11.8组件 =====" -ForegroundColor Cyan
Write‑Host:向控制台直接输出文本(打印消息到屏幕)`n:反引号+n,PowerShell里面的换行转义字符(相当于回车换行)-ForegroundColor Cyan参数:设置文字前景颜色为青色。
💡注意区别:
Write‑Host只是屏幕打印;不会输出到对象数据流,不能被管道接收,仅用于人机提示。
2️⃣ $toUninstall = @()
$toUninstall = @()
@():数组构造语法,创建一个空数组。
用途:后续循环过程中,把筛选出来需要卸载的软件对象,逐个存进这个数组。
后面执行卸载的时候,直接遍历
$toUninstall里面保存的注册表软件对象即可。
3️⃣外层 foreach循环
foreach ($name in $targetList) {
……
}
foreach循环语法:foreach( 迭代变量 in 集合 ) {循环体}
$targetList:源数组(我们预先写好的一组软件名字字符串)$name:循环迭代变量;每一轮循环,$name依次取出targetList里面的一个软件名字字符串。
举例子:如果targetList里面第一项是
"NVIDIA CUDA Development 11.8",本轮$name的值就是这个字符串;下一轮取下一个名称。
循环体内第一句
$matchApp = $allApps | Where-Object { $_.DisplayName -like "*$name*" }
$allApps:全部本机软件对象集合(注册表读出来)| Where‑Object { ... }管道过滤筛选$_.DisplayName:当前软件对象的控制面板显示名称-like "*$name*"-like:通配符模糊匹配运算符*是通配符,代表任意字符;前后都加*→ 包含匹配(只要DisplayName里面包含$name这一段文字就算命中)
⚠️这里是模糊匹配,不是完全相等;不怕版本号微小差异。
- 筛选得到的结果(0个、1个或者多个匹配的软件对象)赋值保存给变量
$matchApp。
重点:PowerShell变量可以自动保存单个对象或者对象数组。
- 如果没有任何匹配结果:
$matchApp = $null- 如果找到1个程序:$matchApp保存这1个对象
- 如果碰巧找到多个符合(null → 条件`)的条目:$matchApp就是对象数组(多条记录)
if ($matchApp) 判断分支
if ($matchApp) {
#找到匹配软件的分支
} else {
#没有找到的分支
}
PowerShell布尔判定规则:
- 如果
$matchApp不为空、不为\(null → \(null → 条件`\)`\)true`,进入if分支(找到了组件) - 如果
$matchApp是空/\(null → `\)false`,进入else分支(打印灰色提示❌未找到)
else分支
Write-Host "❌ 未找到:$name" -ForegroundColor DarkGray
仅仅控制台打印灰色文字提示:本机没有安装这一项组件。
if内部又嵌套一层foreach循环
foreach ($app in $matchApp) {
Write-Host "✅ $($app.DisplayName)"
$toUninstall += $app
}
$matchApp有可能返回多条软件对象;再做一层foreach遍历每一条匹配到的程序。$($app.DisplayName):子表达式展开语法。
在双引号字符串
"✅ $($app.DisplayName)"里面访问对象属性,必须加$();直接写$app.DisplayName不会解析属性。
$()作用:先执行括号里面表达式,把返回值嵌入字符串输出。
$toUninstall += $app
+=数组追加语法:把当前软件对象\(app\(app追加存入预先准备好的\)\)toUninstall数组。
👉❗这里存进去的不是软件名字字符串!是完整的注册表对象(包含DisplayName,最重要是UninstallString卸载命令)。后续卸载阶段直接读取这个对象里面的卸载命令。
📚完整整体执行逻辑(梳理整个流程)
- 先屏幕打印青色标题,提示开始检索CUDA组件;
- 初始化空数组
$toUninstall,用来存放所有待卸载软件对象; - 外层foreach逐个遍历我们预设清单\(targetList里面每一个组件名称字符串:**
3‑1 使用Where‑Object +‑like模糊匹配,在本机全部已安装软件列表`\)allApps
搜索是否存在该软件;结果保存\(matchApp`; 3‑2 如果**没有搜到(\)matchApp为空):控制台打印灰色提示❌未找到,继续下一轮循环;
3‑3 如果搜索命中(找到软件):再开启内层foreach遍历所有命中结果;
- 在控制台打印✅以及该软件的实际显示名称;
- 将完整的软件对象追加存入$toUninstall数组; - 全部外层循环跑完之后:
$toUninstall数组里面就收集好了本机上实际存在的、需要卸载的全部NVIDIA CUDA组件对象;后面卸载循环直接读取这个数组执行卸载。
📌几个容易踩坑的关键点总结
`n反引号转义符,只在双引号字符串里面生效;单引号不会解析转义。"字符串里面访问对象属性必须$()",例如"$($app.DisplayName)";$toUninstall += $app:追加对象,不是追加文本名字;后续卸载要依靠对象里面的UninstallString;- 双层foreach的必要性:虽然绝大多数情况下一条名称只会匹配到1个软件;但兼容万一一条名称匹配多条注册表项的极端情况;内层循环保证所有命中条目全部收集进待卸载列表。
-like "*$name*"模糊包含匹配;如果改成-eq $name就是严格完全相等匹配,版本号稍有差异脚本就识别不到软件了。
片段解读4
后半段:msiexec 卸载循环部分解析(CUDA组件卸载脚本)
先回顾前面脚本已经做完的事情:
$allApps:从系统注册表读取本机已经安装的软件列表$targetList:预设好CUDA‑11.8各个组件名字数组- 双层遍历 +
-like模糊检索,把所有要卸载的软件对象收集到数组$toUninstall👉
$toUninstall里面每一个元素$app保存:DisplayName(软件名)、IdentifyingNumber(MSI产品GUID,卸载编号)
典型的后半段卸载代码示范
foreach($app in $toUninstall)
{
Write‑Host "正在卸载 $($app.DisplayName)" -ForegroundColor Yellow
msiexec /x $app.IdentifyingNumber /qn
}
1、msiexec是什么(外部命令,不是PowerShell内置函数)
msiexec 是 Windows Installer 的系统命令,专门用来处理msi安装包的安装、修复、卸载。
/x {GUID}:执行卸载(eXecute uninstall),后面接MSI产品GUIDIdentifyingNumber/qn:完全静默卸载,不弹出GUI窗口、不需要人手动点下一步确认
如果写成
/qb:会显示简易进度条;不带q参数就会弹出传统卸载向导界面。
💡关键点:NVIDIA CUDA各个组件(开发包、runtime、nsight等)全部都是MSI安装,所以可以直接拿产品GUID调用msiexec卸载,不需要找exe卸载程序。
2、外层foreach循环逻辑
foreach($app in $toUninstall)
依次遍历我们上半段检索收集出来的待卸载软件数组。
每一轮循环
$app就代表其中一个CUDA组件。
$app.DisplayName:取出显示名称,控制台打印日志,方便我们看到现在正在卸载哪一项。
3、msiexec /x $app.IdentifyingNumber /qn 重点语法说明
$app.IdentifyingNumber:从注册表读到的MSI产品GUID(类似{F827D36E‑xxxx‑xxxx‑xxxx‑xxxxxxxxxxxx})- PowerShell会自动把对象属性的值替换进去;最终操作系统实际执行一条类似:
msiexec /x {F827D36E‑8460‑4B94‑A23C‑E2E5D77A9991} /qn
4、执行行为 & 风险点(很重要)
- msiexec 默认是异步!!【大坑】
PowerShell调用msiexec之后,它直接返回,不会等待卸载完成就立刻跑下一条卸载。
也就是多个卸载进程同时并行跑。
NVIDIA CUDA组件之间存在依赖关系!并行卸载容易卸载失败、注册表残留。
✅ 常见改良写法:用 Start‑Process -Wait 强制等待每一个卸载结束再继续下一个:
Start‑Process msiexec -ArgumentList "/x $($app.IdentifyingNumber) /qn" -Wait
-Wait参数:PowerShell阻塞,等本次msiexec卸载进程完全退出之后,再进入下一轮循环卸载下一个CUDA组件。
写清理CUDA脚本强烈建议加上‑Wait,防止并发卸载出错。
5、整体完整逻辑串起来(前半检索 + 后半卸载)
- 读取本机已安装软件列表
$allApps - 根据名字模糊搜索全部CUDA11.8相关组件,收集全部命中项存入
$toUninstall - 遍历待卸载列表,逐个调用msiexec执行静默卸载
- (建议加上‑Wait,串行卸载,规避组件之间卸载冲突)
6、补充两个脚本易错细节
- IdentifyingNumber 这个属性只有MSI安装的程序才有;如果是普通exe安装的软件,该字段为空,这种方式就不能卸载;CUDA套件恰好全部MSI部署,适合该脚本。
/qn静默模式不会弹出报错对话框;如果卸载失败,GUI看不到提示,只能看系统的Windows Installer事件日志排查错误。
片段解读5
问题:为什么运算符是 -like、‑gt,前面带减号 -,不能直接写 like、gt?
先直接结论:
‑like / ‑gt / ‑eq / ‑match不是普通函数、也不是关键字;它们是 PowerShell 专属的比较运算符标记,开头短横-是语法设计的一部分。
如果你直接写like $a $b,PowerShell不会把它识别成比较,会尝试把like解释成一个命令/程序名,直接报错。
一、先看其它脚本语言做对比
🟢 Bash / Python
Python:a > 5,in;符号或者单词直接作为语言内置关键字。
if name in text:
if x > 10:
🟡 PowerShell 的历史背景(设计者视角)
PowerShell底层是Shell +脚本语言二合一。
Shell的第一要务:命令优先!
PowerShell设计哲学第一条:命令(Cmdlet)优先解析。
任意一串英文单词,解释器优先尝试把它当做一条外部命令、函数、cmdlet去运行。
设想一下,如果设计者把比较运算符直接设计成裸单词:
#【假想的错误语法,并不存在】
if ($x gt 5)
当解析器读到单词gt:
解释器第一反应:gt是不是一个可执行程序?是不是一个函数?是不是cmdlet?
解释器会优先去PATH里面搜索名叫gt的命令。
于是产生歧义!分不清这到底是:
- 一条名叫
gt的外部命令; - 还是脚本语言里面的“大于”比较运算符。
⚠️ 歧义是Shell语言最大的坑。
Shell本来就是用来跑外部程序的;大量简短英文名字的可执行文件随处可见。
如果比较运算符直接使用裸英文单词,随时会和用户写的命令、外部exe重名冲突。
👉 所以设计者选择统一约定:所有这类语言内部运算符,名称前面强制增加短横线‑。
‑gt、‑lt、‑eq、‑ne、‑like、‑match、‑contains。
开头‑就是一个识别标记!告诉解析器:这不是一条命令,这是语言内置运算符!不要去磁盘搜索同名程序。
二、第二点设计:统一风格,和Cmdlet参数保持形式一致
PowerShell cmdlet 的参数全部以短横线开头:
Get‑ChildItem -Recurse -Filter *.txt
Where‑Object -Property Name -like "abc*"
设计者希望整个语言符号视觉统一:
-XXX代表“语言内部标记”:既用于cmdlet的命名参数,也用于运算符。
看到开头横杠,解释器立刻知道:这是语言层面的符号,不是要启动外部命令。
补充历史:PowerShell的前身Monad团队设计的时候刻意放弃了C‑style的
> < ==符号。
为什么不完全用><?
因为>在shell里面已经被占用为重定向输出符号!
dir > out.txt # >是输出重定向!
❗大于符号>已经被shell拿做重定向;小于<用于输入重定向。
所以不能再拿>当做数字大于的比较运算符!
于是只能改用英文单词描述语义:
- greater‑than →
‑gt - less‑than →
‑lt - equal →
‑eq - like(通配符模糊匹配)→
‑like
这就是为什么不用
>,反而要用-gt。
普通编程语言if($a>10);powershell必须写if($a -gt 10)。
三、第三点:语义可读性(面向运维人员,不是C程序员)
微软当时的目标用户是Windows系统管理员(运维),很多人不熟悉编程符号。
用英文缩写单词而不是符号:
‑gt = greater than(大于)
‑lt = less than(小于)
‑like = like(像、模糊匹配)
即使不懂编程,看到单词大概能猜含义;短横‑只是前缀标识。
四、反面演示,试一下去掉横杠会发生什么
如果你故意写,不带横杠:
10 like 8
PowerShell解析:like是命令名称。
它会尝试去找一个叫like.exe的程序,直接报错:
like : 无法将“like”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。
完全无法识别成比较运算。
五、顺带提简化语法(Where‑Object简写形式)
$allApps | Where‑Object DisplayName -like "*CUDA*"
这里 -like依然保留减号。即使省略脚本块{}的简化写法,运算符前面的‑不能丢掉。
✨总结,从设计者视角梳理三条核心动机
- 消除Shell解析歧义(最重要)
Shell优先解析单词为外部命令;‑作为前置标记,告诉解释器:这是内置运算符,不要搜索外部exe。避免运算符名字和可执行文件名冲突。 - Shell重定向符号已经抢占了
><
>、<已经用于输出、输入重定向,不能复用做大小比较;改用英文单词缩写形式。 - 语言风格统一 + 面向运维人员可读性
整个环境里面,参数、运算符统一使用‑开头;使用英文缩写,降低运维人员记忆门槛。
补充记忆点:
在PowerShell中,凡是这种用来做条件判断的:‑eq,‑ne,‑gt,‑lt,‑ge,‑le,‑like,‑notlike,‑match,‑notmatch,‑contains,全部必须带前面的短横杠‑。

浙公网安备 33010602011771号