ForEach-Object 不筛选而负责处理,Where-Object(where)才执行筛选;两者常组合实现“先筛后处理”,如 Get-Service | Where Status -eq Running | foreach DisplayName,Status。
powershell 中用管道符配合 foreach-object(常简写为 foreach)做“快速筛选”其实是个常见误解——foreach-object 本身不筛选,它负责“对每个对象执行操作”;真正起筛选作用的是 where-object(或 where)。但两者常搭配使用,形成“先筛后处理”的高效组合。
明确角色:where 筛、foreach 处理
管道中各命令职责分明:
-
Where-Object(where):只保留满足条件的对象,从管道中“删掉”不匹配的项 -
ForEach-Object(foreach):对管道中剩下的每个对象执行计算、提取属性、调用方法或构造新数据
例如,想查出所有运行中且名称含 “win” 的服务,并只显示其显示名和状态:
Get-Service | Where Status -eq Running | Where Name -like *win* | ForEach DisplayName,Status
注意最后这句 ForEach DisplayName,Status 是简化语法,等价于 ForEach-Object {$_.DisplayName,$_.Status},输出的是两个字段拼成的数组,适合后续处理。
用简化语法提速:省略 $_ 和脚本块
PowerShell 3.0+ 支持自然语言风格的简化语法,让 foreach 更轻量:
- 提取单个属性:
Get-Process | foreach ProcessName - 提取多个属性:
Get-Process | foreach ProcessName,Id,CPU - 调用方法:
Get-ChildItem *.log | foreach LastWriteTime.ToString("yyyy-MM-dd") - 注意:属性名必须真实存在,拼错就返回空,不会报错
避免常见陷阱:顺序和性能
筛选必须在处理之前完成,否则可能因属性缺失导致意外空结果:
- ❌ 错误:先选属性再筛不存在的字段
Get-Service | Select DisplayName,Status | Where CanPauseAndContinue
→CanPauseAndContinue已被Select丢弃,筛选永远失败 - ✅ 正确:先筛后选或后处理
Get-Service | Where CanPauseAndContinue | Select DisplayName,Status
或Get-Service | Where CanPauseAndContinue | foreach DisplayName,Status - 小技巧:用
Measure-Object快速验证筛选效果Get-Service | Where Status -eq Running | Measure
进阶组合:一行完成筛选+转换+导出
实际运维中常需链式处理。比如导出最近7天修改的 .ps1 文件名和大小(KB):
Get-ChildItem *.ps1 -File |
Where LastWriteTime -gt (Get-Date).AddDays(-7) |
foreach @{Name='FileName';Expression={$_.Name}},@{Name='SizeKB';Expression={[math]::Round($_.Length/1KB,2)}} |
Export-Csv recent-scripts.csv -NoTypeInformation这里 foreach 用了哈希表自定义属性,实现字段重命名和单位换算,比写完整脚本块更紧凑。


















