PowerShell 和 Bash 最大的分歧不在语法,而在设计哲学。Bash 管道传递文本,PowerShell 管道传递对象。这一个决定带来了完全不同的工作方式:你不需要用 grep 和 awk 从格式化输出里抠数据,你直接访问对象的属性。
1. 定位与场景
PowerShell 起初是 Windows 管理的专属工具,CMD 时代留下了太多只能做批处理的遗憾。微软在 2006 年推出 PowerShell,让系统管理员能够通过结构化的脚本操作注册表、服务、网络、Active Directory 和 Windows 组件。2016 年开源并跨平台,PowerShell Core(即今天的 pwsh)可以在 macOS 和 Linux 上运行。
什么时候该用 PowerShell:
- Windows 系统管理:服务、进程、事件日志、注册表、用户账户
- Microsoft 生态自动化:Microsoft 365、Azure、Exchange、Entra ID 都有官方 PowerShell 模块
- 跨平台脚本需要处理结构化数据,且不想写 Python 的时候
- 团队协作环境以 Windows 为主,或 CI/CD 跑在 Windows runner 上
什么时候 Bash/Zsh 更合适:操作的是 Linux 服务器、需要接入大量 Unix 工具链,或者文本处理已经够用的场景,Bash 仍然是更自然的选择。两者不是互斥的,很多工程师在 macOS 上同时装了两个。
| 维度 | PowerShell | Bash / Zsh |
|---|---|---|
| 管道传递 | 结构化对象 | 纯文本 |
| 主力平台 | Windows 优先,跨平台可用 | Unix/Linux/macOS |
| 脚本扩展名 | .ps1 | .sh / .zsh |
| 系统 API 访问 | .NET 对象模型,原生调 Windows API | 文件、环境变量、syscall |
| 错误处理 | try/catch,异常对象 | $?,退出码,trap |
| 最适合场景 | Windows 管理、Microsoft 云服务 | 服务器运维、文本流处理 |
2. 环境入口
PowerShell 有两个版本在流通:
powershell.exe:Windows 内置版本(PowerShell 5.x),仅 Windows,基于 .NET Frameworkpwsh:PowerShell 7+,跨平台,基于 .NET 6+,功能更完整,推荐使用
在 macOS/Linux 上通过包管理器安装 pwsh;Windows 上从 Microsoft Store 或 GitHub Release 安装。如果只看到 powershell 命令,检查是否需要安装新版本。
执行策略是进入 PowerShell 脚本世界必须过的第一道门。默认策略 Restricted 禁止执行任何脚本。常见设置:
# 允许本地脚本,要求远程脚本有数字签名
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser执行策略不是安全机制,而是误操作防护——它阻止你双击运行了一个来路不明的 .ps1,但对有意识运行的脚本没有约束力。真正的边界仍然是脚本来源和权限管控。
3. Cmdlet 命名规范
PowerShell 的命令叫 Cmdlet(读作 command-let),命名遵循严格的 Verb-Noun 格式。动词来自官方的 approved verb list,名词描述操作对象:
Get-Process # 获取进程列表
Set-Location # 切换目录(等同于 cd)
New-Item # 创建文件或目录
Remove-Item # 删除文件或目录
Start-Process # 启动进程
Stop-Service # 停止服务常用动词:
| 动词 | 含义 | 典型用途 |
|---|---|---|
Get | 获取信息 | Get-Process、Get-Service、Get-ChildItem |
Set | 设置或修改 | Set-Location、Set-Item、Set-Content |
New | 创建对象 | New-Item、New-Object、New-Module |
Remove | 删除对象 | Remove-Item、Remove-Job |
Start | 启动 | Start-Process、Start-Service、Start-Job |
Stop | 停止 | Stop-Process、Stop-Service |
Test | 检测或验证 | Test-Path、Test-Connection |
Export | 导出数据 | Export-Csv、Export-Json |
Import | 导入数据 | Import-Csv、Import-Module |
Invoke | 执行操作 | Invoke-Command、Invoke-WebRequest |
Verb-Noun 不只是命名约定,它也是导航系统。不确定命令名时,先想动词和名词,再用 Get-Command 搜索(见第 9 节)。PowerShell 还提供了很多传统别名(ls、cd、cat、dir)让 Unix 用户有过渡期,但在脚本里应该用正式的 Cmdlet 名,别名在不同平台上不保证一致。
4. 对象管道
PowerShell 管道是理解这门语言的核心。Unix 管道把文本从左传到右,PowerShell 管道把对象从左传到右。
# Unix 风格:Get-Process 输出文本 → grep 过滤文本行
# PowerShell 风格:Get-Process 输出进程对象 → Where-Object 过滤对象
Get-Process | Where-Object { $_.CPU -gt 100 }$_ 是当前管道对象,在 {} 块内用点号访问属性:
Get-Process | Where-Object { $_.Name -eq "chrome" }
Get-Service | Where-Object { $_.Status -eq "Stopped" } | Start-Service几个常用的管道操作 Cmdlet:
Where-Object:按条件过滤,等同于 grep 但操作对象属性而不是文本:
Get-Process | Where-Object CPU -gt 100
# 新语法可以省略 {} 和 $_.,更简洁Select-Object:选择属性,等同于 awk 取列:
Get-Process | Select-Object Name, CPU, WorkingSet | Sort-Object CPU -DescendingSort-Object:排序:
Get-ChildItem | Sort-Object Length -Descending | Select-Object -First 10Get-Member:查看对象有哪些属性和方法,是探索陌生 Cmdlet 输出的利器:
Get-Process | Get-Member
# 输出:TypeName、Properties、Methods管道里传递的始终是真实对象,不是格式化后的显示文本。不要在管道末尾对表格文本做字符串解析,那是在绕过 PowerShell 最大的优势。
5. 变量与数据类型
变量以 $ 开头,动态类型,无需声明:
$name = "Alice"
$age = 30
$rate = 3.14字符串支持双引号插值:
Write-Output "Hello, $name! You are $age years old."
# 输出:Hello, Alice! You are 30 years old.单引号是字面量,不做插值:
Write-Output 'Hello, $name'
# 输出:Hello, $name数组:
$fruits = @("Apple", "Banana", "Cherry")
$fruits[0] # Apple
$fruits.Count # 3
$fruits += "Durian" # 追加元素哈希表(类似 Python dict):
$config = @{
Host = "localhost"
Port = 5432
DB = "mydb"
}
$config["Host"] # localhost
$config.Port # 5432,点号访问同样有效6. 运算符
比较运算符用英文缩写而不是符号,避免和 XML/HTML 的 > 冲突:
| 运算符 | 含义 | 示例 |
|---|---|---|
-eq | 等于 | $a -eq $b |
-ne | 不等于 | $a -ne $b |
-gt | 大于 | $a -gt 100 |
-ge | 大于或等于 | $a -ge 0 |
-lt | 小于 | $a -lt 0 |
-le | 小于或等于 | $count -le 10 |
逻辑运算符:
($a -gt 0) -and ($b -lt 100)
($status -eq "Running") -or ($status -eq "Pending")
-not $isAdmin模式匹配运算符:
"PowerShell" -match "Power" # 正则匹配,返回 True/False
"PowerShell" -like "Power*" # 通配符匹配,* 匹配任意字符
@(1, 2, 3) -contains 2 # 检查数组是否包含元素算术运算符与大多数语言相同:+、-、*、/、%(取模)。字符串加法是拼接:"Hello" + " World" 得到 "Hello World"。
7. 流程控制
条件语句:
if ($age -ge 18) {
Write-Output "Adult"
} elseif ($age -ge 13) {
Write-Output "Teen"
} else {
Write-Output "Child"
}switch:多分支匹配,比 if-elseif 链更清晰:
switch ($day) {
"Monday" { Write-Output "Week starts" }
"Friday" { Write-Output "Almost weekend" }
"Saturday" { Write-Output "Weekend" }
"Sunday" { Write-Output "Weekend" }
default { Write-Output "Midweek" }
}循环:
# for:适合已知次数的迭代
for ($i = 0; $i -lt 5; $i++) {
Write-Output "Iteration $i"
}
# foreach:遍历集合,最常用
$services = Get-Service
foreach ($svc in $services) {
if ($svc.Status -eq "Stopped") {
Write-Output "Stopped: $($svc.Name)"
}
}
# 管道中的 foreach 变体(ForEach-Object)
Get-Service | ForEach-Object { Write-Output $_.Name }
# while:条件控制
$i = 0
while ($i -lt 3) {
Write-Output $i
$i++
}8. 脚本基础
PowerShell 脚本文件扩展名为 .ps1。执行时需要用 .\ 前缀(当前目录),不能直接输入文件名:
.\hello.ps1参数定义通过 param() 块:
# deploy.ps1
param (
[string]$Environment = "staging",
[int]$Port = 8080,
[switch]$DryRun # 开关型参数,不传则为 $false
)
Write-Output "Deploying to $Environment on port $Port"
if ($DryRun) {
Write-Output "[DryRun] No actual deployment"
}调用时使用具名参数:
.\deploy.ps1 -Environment production -Port 443
.\deploy.ps1 -Environment staging -DryRun注释:单行用 #,多行用 <# ... #>:
# 这是单行注释
<#
多行注释块
可以跨多行
#>脚本里的字符串插值需要注意:$() 是子表达式,访问对象属性时要用它:
Write-Output "Service: $($svc.Name) - Status: $($svc.Status)"
# 不是 "Service: $svc.Name",那样只会插值 $svc 然后拼 ".Name"9. 获取帮助
PowerShell 的帮助系统是内置的,不需要开浏览器:
# 查看 Cmdlet 的帮助页面
Get-Help Get-Process
# 查看详细帮助(含参数说明)
Get-Help Get-Process -Detailed
# 查看示例
Get-Help Get-Process -Examples
# 搜索包含关键词的 Cmdlet
Get-Help *process*
# 更新本地帮助文档(需要网络)
Update-Help按动词或名词搜索 Cmdlet:
# 列出所有 Get 动词的 Cmdlet
Get-Command -Verb Get
# 列出所有操作 Process 的 Cmdlet
Get-Command -Noun Process
# 搜索名字包含 "service" 的 Cmdlet
Get-Command *service*探索对象结构:
# 看某个 Cmdlet 输出了哪些属性和方法
Get-Process | Get-Member
# 看具体属性值(选几个感兴趣的列)
Get-Process | Select-Object Name, Id, CPU, WorkingSet | Format-Table初学阶段,Get-Help、Get-Command 和 Get-Member 这三个 Cmdlet 能解答 80% 的”这个怎么用”问题。遇到陌生的对象先 Get-Member,不知道命令名先 Get-Command,知道命令名但不知道参数先 Get-Help。
10. 使用原则
先看对象,再看显示:Get-Process 在终端里展示成表格,但管道里是对象。终端显示是为了人眼阅读,管道里传递的不是那张表格的文本,而是底层的 .NET 对象。不确定对象有哪些属性时,Get-Member 是第一反应,不要解析表格文本。
Verb-Noun 是导航,不是背诵:不需要记住所有 Cmdlet。想获取什么就 Get-Command -Noun <名词>,想执行什么操作就 Get-Command -Verb <动词>。多数情况下你能推导出命令名,然后用 Get-Help 确认参数。
脚本要有参数,不要硬编码:把环境名、路径、端口写进 param() 而不是直接写死在脚本里。这让脚本可以被 CI、Agent 或其他人复用,不需要修改脚本本体。
执行策略不是安全边界:不要因为执行策略允许就运行来路不明的脚本。真正的判断依据是:这段脚本从哪来、做了什么、改动了哪些系统资源。在生产环境执行脚本前,先在测试环境验证,参数加 -WhatIf 或写 DryRun 逻辑,确认行为符合预期再去掉。
对象流适合自动化消费:如果脚本的输出会被另一个程序、脚本或 Agent 消费,输出对象而不是格式化文本。需要给外部系统消费时,用 ConvertTo-Json 导出结构化数据,比解析人类可读的表格稳定得多。
跨平台注意平台差异:用 pwsh 写跨平台脚本时,路径分隔符用 [IO.Path]::Combine() 或 Join-Path,不要硬写 \;注册表、服务管理、Windows 事件日志等 Cmdlet 只在 Windows 上有效,跨平台脚本要用 $IsWindows、$IsMacOS、$IsLinux 做判断。