写在前面
PowerShell 的关键不是“另一种 Shell 语法”,而是对象管道。Bash 和 CMD 通常在命令间传递文本,PowerShell 则传递带类型、属性和方法的 .NET 对象。理解这一点,Where-Object、Select-Object 和 Get-Member 就不再是需要死记的命令。
本文以跨平台 PowerShell 7 为主。Windows 自带的 Windows PowerShell 5.1 与 PowerShell 7 可以并存,前者通常由 powershell.exe 启动,后者由 pwsh 启动。
系列导航:上一篇:Windows Batch · 下一篇:Bash Shell
一、Cmdlet 与发现式学习
Cmdlet 通常采用 Verb-Noun 命名:
1
2
3
4
5
| Get-Process
Get-Service
Get-ChildItem
Set-Location
Remove-Item
|
不要先背整张命令表,而是学会发现:
1
2
3
| Get-Command -Noun Service
Get-Help Get-ChildItem -Examples
Get-Process | Get-Member
|
Get-Command 找命令,Get-Help 看用法,Get-Member 看管道中对象的类型、属性和方法。
二、变量、集合与哈希表
1
2
3
4
5
6
7
8
9
10
| $name = 'demo'
$count = 3
$enabled = $true
$nothing = $null
$numbers = 1, 2, 3
$config = @{
Environment = 'production'
Port = 8080
}
|
PowerShell 变量以 $ 开头,默认可动态变更类型,也可显式约束:
1
2
3
| [int]$port = 8080
[datetime]$startedAt = Get-Date
[string[]]$names = 'api', 'worker'
|
自定义结构化数据可以用 [pscustomobject]:
1
2
3
4
5
6
7
| $app = [pscustomobject]@{
Name = 'orders-api'
Port = 8080
Healthy = $true
}
$app.Name
|
三、字符串与展开
1
2
3
4
| $name = 'PowerShell'
'Hello, $name' # 不展开
"Hello, $name" # 展开
"2 + 3 = $(2 + 3)" # 子表达式
|
多行字符串使用 Here-string:
1
2
3
4
5
6
| $json = @'
{
"name": "demo",
"enabled": true
}
'@
|
路径优先用 Join-Path,脚本目录使用 $PSScriptRoot:
1
| $output = Join-Path $PSScriptRoot 'artifacts'
|
四、条件、循环与 switch
1
2
3
4
5
6
7
| if ($port -ge 1 -and $port -le 65535) {
'valid port'
} elseif ($port -eq 0) {
'automatic port'
} else {
throw "Invalid port: $port"
}
|
常见比较符有 -eq、-ne、-gt、-ge、-lt、-le、-like 和 -match。
1
2
3
4
5
6
7
8
9
10
11
| foreach ($file in Get-ChildItem -Filter *.md) {
$file.FullName
}
1..5 | ForEach-Object { $_ * 2 }
switch -Regex ($status) {
'^2\d\d$' { 'success'; break }
'^4\d\d$' { 'client error'; break }
default { 'other' }
}
|
foreach 是语言关键字,ForEach-Object 是管道命令,两者不是一回事。
五、对象管道
1
2
3
4
| Get-Process |
Where-Object CPU -GT 10 |
Sort-Object CPU -Descending |
Select-Object -First 5 Name, Id, CPU
|
Get-Process 输出的不是已排版的表格字符串,而是进程对象。后续命令直接读取 CPU、Name 等属性,无需切割文本列。
1
2
3
4
| Get-ChildItem -File -Recurse |
Where-Object Length -GT 10MB |
Select-Object FullName, Length, LastWriteTime |
Export-Csv large-files.csv -NoTypeInformation
|
Format-Table 和 Format-List 用于最终展示,通常应放在管道末端,不要在中途把格式化对象交给数据处理命令。
六、函数与参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| function Get-LargeFile {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path,
[ValidateRange(1, 10240)]
[int]$MinimumMB = 100
)
Get-ChildItem -LiteralPath $Path -File -Recurse |
Where-Object Length -GE ($MinimumMB * 1MB)
}
Get-LargeFile -Path . -MinimumMB 20
|
可接收管道输入的高级函数可使用 begin、process、end 和 PowerShell 7.3 引入的 clean 块。大多数普通脚本先写清晰的 param() 就足够了。
七、文件、JSON 与 HTTP
1
2
3
4
5
6
| $config = Get-Content -LiteralPath ./appsettings.json -Raw |
ConvertFrom-Json
$config.Logging.LogLevel.Default = 'Warning'
$config | ConvertTo-Json -Depth 10 |
Set-Content -LiteralPath ./appsettings.generated.json -Encoding utf8
|
1
2
3
4
5
6
| $request = @{
Uri = 'https://api.example.com/health'
Method = 'Get'
}
$result = Invoke-RestMethod @request
$result.status
|
PowerShell 适合自动化的重要原因,就是 JSON、CSV、HTTP 和文件系统都能直接变成对象处理。PowerShell 不使用 Bash 的反斜杠续行;参数较多时,splatting 通常比行尾反引号更清晰。
八、错误处理与原生程序
1
2
3
4
5
6
7
8
| try {
Get-Content -LiteralPath ./missing.txt -ErrorAction Stop
} catch {
Write-Error "Read failed: $($_.Exception.Message)"
exit 1
} finally {
'cleanup'
}
|
PowerShell 区分终止错误和非终止错误,需要 catch 的 Cmdlet 调用常配合 -ErrorAction Stop。
调用 git、dotnet 等原生程序后,检查 $LASTEXITCODE:
1
2
3
4
| dotnet test
if ($LASTEXITCODE -ne 0) {
throw "dotnet test failed: $LASTEXITCODE"
}
|
PowerShell 7 支持 && 和 || 管道链操作符:
1
| dotnet test && dotnet publish -c Release
|
九、完整实战:生成文件清单
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
| [CmdletBinding()]
param(
[string]$Path = $PSScriptRoot,
[string]$Output = (Join-Path $PSScriptRoot 'files.json')
)
$ErrorActionPreference = 'Stop'
try {
$root = (Resolve-Path -LiteralPath $Path).Path
$items = Get-ChildItem -LiteralPath $root -File -Recurse |
Select-Object @{Name='Path'; Expression={
[IO.Path]::GetRelativePath($root, $_.FullName)
}}, Length, LastWriteTimeUtc
$items |
ConvertTo-Json -Depth 3 |
Set-Content -LiteralPath $Output -Encoding utf8
Write-Host "Generated $Output ($($items.Count) files)"
} catch {
Write-Error $_
exit 1
}
|
这段脚本展示了 PowerShell 的典型优势:文件本来就是对象,投影后可以直接序列化成 JSON,不需要手工拼接文本。
十、快速选择
- 服务和进程:
Get-Service、Get-Process - 文件:
Get-ChildItem、Copy-Item、Move-Item、Remove-Item - 过滤:
Where-Object - 投影:
Select-Object - 排序与分组:
Sort-Object、Group-Object - 数据交换:
ConvertFrom-Json、ConvertTo-Json、Import-Csv、Export-Csv - HTTP:
Invoke-RestMethod、Invoke-WebRequest - 自我发现:
Get-Command、Get-Help、Get-Member
PowerShell 脚本的可维护性来自保留对象,而不是过早把一切变成文本。在管道中处理对象,在边界上输入或输出 JSON、CSV 和格式化文本,是最值得保留的设计原则。
系列导航:上一篇:Windows Batch · 下一篇:Bash Shell
参考资料