PowerShell 的管道(Pipeline)和过滤是 PowerShell 最强大、最核心的功能之一,几乎所有高级脚本都离不开它们。下面系统性地讲解管道原理和各种过滤方式。
1. PowerShell 管道的核心机制(和其他壳的巨大区别)
| 项目 | PowerShell 管道 | Linux Bash / cmd 管道 |
|---|---|---|
| 传递的内容 | 对象(.NET Object) | 纯文本(string) |
| 管道符号 | | | | |
| 左边命令输出 | 完整的对象(含属性和方法) | 只剩 ToString() 后的文本 |
| 右边命令接收 | 参数自动按属性绑定(ByPropertyName) | 只能从 stdin 读文本 |
例子:
# 这条命令输出的不是文本,而是 Process 对象
Get-Process | Where-Object {$_.CPU -gt 100}
# Linux 等价写法必须用文本解析,极其脆弱
ps -aux | grep chrome | awk '{if($3>10) print}'
2. 管道中常见的过滤命令(性能从高到低排序)
| 命令 | 推荐场景 | 性能特点 | 例子 |
|---|---|---|---|
| Where-Object (过滤位置) | 强烈推荐,只要能用就用这个 | 在管道最左边过滤,性能最高 | Get-Process |
| Select-Object -First/-Last/-Skip | 取前N条、后N条、跳过前N条 | 也是流式处理,性能极高 | Get-ChildItem |
| Where-Object (普通) | 复杂条件,无法用参数过滤时使用 | 每条记录都进入管道,性能中等 | Get-Service |
| ForEach-Object | 需要对每条记录做复杂操作或副作用 | 性能较差,避免在超大集合中使用 | Get-ChildItem |
| .Where() 方法 (PowerShell 7+) | 最现代、最快的过滤方式 | 比 Where-Object 快 5-50 倍 | (Get-Process).Where{$_.CPU -gt 100} |
| .Where() 加速模式 | 超大数据集推荐 | 极快,支持 Acceleration | (Get-EventLog -LogName System -Newest 100000).Where({$_.EntryType -eq ‘Error’}, ‘Split’) |
3. 最佳实践:尽可能用“参数过滤”而不是管道过滤
很多 cmdlet 支持直接在参数层面过滤,比管道快几十到几百倍!
# 慢(读取所有进程再过滤)
Get-Process | Where-Object Name -eq "chrome"
# 快(只返回 chrome 进程,推荐!)
Get-Process -Name chrome
# 更多例子
Get-Service -Name w* # 比 Get-Service | Where Name -like "w*" 快很多
Get-ChildItem -Path C:\ -Recurse -File -Filter "*.log" # -Filter 是提供者级过滤,最快
Get-EventLog -LogName System -EntryType Error # 直接过滤
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624} -MaxEvents 100 # 最快方式
4. 实战常用的管道过滤组合
# 1. 找出占用 CPU 前 10 的进程(经典)
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
# 2. 找出最近 3 天修改的 .log 文件并按大小倒序
Get-ChildItem D:\Logs -Recurse -File -Filter "*.log" |
Where-Object LastWriteTime -gt (Get-Date).AddDays(-3) |
Sort-Object Length -Descending
# 3. PowerShell 7+ 推荐写法(更快、更清晰)
(Get-ChildItem D:\Logs -Recurse -File).Where{
$_.Extension -eq '.log' -and $_.LastWriteTime -gt (Get-Date).AddDays(-3)
} | Sort-Object Length -Descending | Select-Object -First 20
# 4. 只显示错误事件的前 50 条(加速模式)
(Get-EventLog System -ErrorAction SilentlyContinue).Where(
{$_.EntryType -eq 'Error'}, 'First', 50
)
5. 管道链性能优化黄金法则(记住这几条就够了)
- 越左边过滤越好(让管道尽早变小)
- 优先使用 cmdlet 的参数过滤(-Name, -Filter, -Include 等)
- 用 Where-Object 放在管道最前面,而不是最后
- 大集合用 .Where() 方法(PS 7+)
- 避免在管道里用 ForEach-Object 做简单过滤,改用 Where-Object
- 需要 Select-Object 时尽早放(减少后面传递的属性量)
6. 一条终极性能对比(10万条数据)
# 慢 100倍(3-8秒)
1..100000 | ForEach-Object { $_ % 100 -eq 0 }
# 中等(0.3-0.8秒)
1..100000 | Where-Object { $_ % 100 -eq 0 }
# 最快(0.03-0.06秒,PS7+)
(1..100000).Where{ $_ % 100 -eq 0 }
掌握了上面这些,你写出来的 PowerShell 脚本既优雅又飞快。有什么具体场景需要优化管道,欢迎贴代码,我帮你改得更快!