适用于 PowerShell 5.1 及以上版本
管道(Pipeline)是 PowerShell 最核心的设计理念——不同于 Unix Shell 的文本管道,PowerShell 传递的是完整的 .NET 对象。这意味着管道中的每个命令都可以访问对象的属性和方法,无需正则表达式解析。然而,很多用户只停留在 | Format-Table 的层面,不了解管道的流式处理特性、自定义管道函数、管道变量等高级功能。
本文将深入讲解 PowerShell 管道的高级技巧和性能优化。
管道绑定参数
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
|
$csvData = @" Name,Id,Status SRV01,101,Running SRV02,102,Stopped SRV03,103,Running "@ | ConvertFrom-Csv
$csvData | ForEach-Object { Write-Host "服务器 $($_.Name) (ID: $($_.Id)) 状态:$($_.Status)" }
function Set-ServerStatus { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [string]$ServerName,
[Parameter(ValueFromPipelineByPropertyName)] [string]$Status = "Unknown" )
process { $icon = if ($Status -eq "Running") { "[OK]" } else { "[!!]" } Write-Host "$icon $ServerName - $Status" -ForegroundColor $(if ($Status -eq "Running") { "Green" } else { "Yellow" }) } }
$csvData | Set-ServerStatus
Set-ServerStatus -ServerName "SRV04" -Status "Running"
|
执行结果示例:
1 2 3 4 5 6 7
| 服务器 SRV01 (ID: 101) 状态:Running 服务器 SRV02 (ID: 102) 状态:Stopped 服务器 SRV03 (ID: 103) 状态:Running [OK] SRV01 - Running [!!] SRV02 - Stopped [OK] SRV03 - Running [OK] SRV04 - Running
|
process 块与流式处理
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
| function Measure-ServerHealth { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [string]$ComputerName )
begin { $total = 0 $healthy = 0 Write-Host "开始健康检查..." -ForegroundColor Cyan }
process { $total++ $isOnline = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet -ErrorAction SilentlyContinue
if ($isOnline) { $healthy++ Write-Host " $ComputerName : 在线" -ForegroundColor Green } else { Write-Host " $ComputerName : 离线" -ForegroundColor Red }
[PSCustomObject]@{ Computer = $ComputerName Online = $isOnline } }
end { $rate = if ($total -gt 0) { [math]::Round($healthy / $total * 100, 1) } else { 0 } Write-Host "检查完成:$healthy/$total 在线($rate%)" -ForegroundColor Cyan } }
@("SRV01", "SRV02", "SRV03", "SRV04", "SRV05") | Measure-ServerHealth
@("SRV01", "SRV02") | Measure-ServerHealth | Where-Object { $_.Online } | Select-Object -ExpandProperty Computer
|
执行结果示例:
1 2 3 4 5 6 7
| 开始健康检查... SRV01 : 在线 SRV02 : 在线 SRV03 : 离线 SRV04 : 在线 SRV05 : 在线 检查完成:4/5 在线(80.0%)
|
管道性能优化
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
| $numbers = 1..10000
$sw1 = [System.Diagnostics.Stopwatch]::StartNew() $result1 = $numbers | ForEach-Object { $_ * 2 } $sw1.Stop() Write-Host "管道方式:$($sw1.ElapsedMilliseconds)ms,结果数:$($result1.Count)"
$sw2 = [System.Diagnostics.Stopwatch]::StartNew() $result2 = $numbers | ForEach-Object -Parallel { $_ * 2 } -ThrottleLimit 8 $sw2.Stop() Write-Host "并行方式:$($sw2.ElapsedMilliseconds)ms,结果数:$($result2.Count)"
$sw3 = [System.Diagnostics.Stopwatch]::StartNew() $result3 = foreach ($n in $numbers) { $n * 2 } $sw3.Stop() Write-Host "赋值语法:$($sw3.ElapsedMilliseconds)ms,结果数:$($result3.Count)"
Add-Type -AssemblyName System.Linq $sw4 = [System.Diagnostics.Stopwatch]::StartNew() $result4 = [System.Linq.Enumerable]::Select( [int[]]$numbers, [Func[int, int]]{ param($x); $x * 2 } ) $sw4.Stop() Write-Host "LINQ 方式:$($sw4.ElapsedMilliseconds)ms,结果数:$($result4.Count)"
$sw = [System.Diagnostics.Stopwatch]::StartNew() 1..100 | ForEach-Object { $result = Invoke-RestMethod "https://httpbin.org/get" -ErrorAction SilentlyContinue } $sw.Stop()
|
执行结果示例:
1 2 3 4
| 管道方式:85ms,结果数:10000 并行方式:42ms,结果数:10000 赋值语法:12ms,结果数:10000 LINQ 方式:3ms,结果数:10000
|
自定义管道命令组合
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
| function Get-LogEntry { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Path,
[int]$Tail = 100 )
process { Get-Content $Path -Tail $Tail -ErrorAction Stop | ForEach-Object { $parts = $_ -split '\s+', 4 if ($parts.Count -ge 4) { [PSCustomObject]@{ Timestamp = $parts[0] + ' ' + $parts[1] Level = $parts[2].Trim('[]') Message = $parts[3] } } } } }
function Where-LogLevel { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] $LogEntry,
[ValidateSet("ERROR", "WARN", "INFO", "DEBUG")] [string[]]$Level )
process { if ($LogEntry.Level -in $Level) { $LogEntry } } }
function Select-RecentErrors { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] $LogEntry,
[int]$Minutes = 60 )
begin { $cutoff = (Get-Date).AddMinutes(-$Minutes) }
process { $timestamp = [datetime]::Parse($LogEntry.Timestamp) if ($timestamp -ge $cutoff) { $LogEntry } } }
Get-LogEntry -Path "C:\Logs\app.log" -Tail 500 | Where-LogLevel -Level "ERROR", "WARN" | Select-RecentErrors -Minutes 30 | Format-Table -AutoSize
|
执行结果示例:
1 2 3 4 5
| Timestamp Level Message
2025-07-08 08:15:30 ERROR Database connection timeout after 30s 2025-07-08 08:20:45 WARN Memory usage above 80% threshold 2025-07-08 08:25:10 ERROR Failed to process message from queue
|
注意事项
- 内存 vs 速度:管道流式处理内存占用低但速度慢,数组赋值速度快但需要全部加载到内存
- PipelineVariable:PowerShell 5.0+ 支持
-PipelineVariable 保存管道中间结果供后续使用
- OutVariable:使用
-OutVariable 参数在管道传递的同时收集输出到变量
- 避免嵌套管道:在
ForEach-Object 中使用管道会导致性能急剧下降
- $_ vs $PSItem:两者等价,
$_ 是 $PSItem 的别名,表示当前管道对象
- 并行管道:
ForEach-Object -Parallel(PowerShell 7+)使用新的运行空间,变量需要用 $using: 传递