[玩转系统] PowerShell ForEach-Object 和 ForEach 循环解释
作者:精品下载站 日期:2024-12-14 03:45:17 浏览:14 分类:玩电脑
PowerShell ForEach-Object 和 ForEach 循环解释
PowerShell ForEach 和 ForEach-Objects 循环是最常用的函数之一。它们使您可以轻松地迭代集合。但是 ForEach-Object
和 ForEach()
cmdlet 之间有什么区别呢?
这两个 cmdlet 看起来很相似,基本上,它们允许您执行相同的操作。但了解两者之间的区别可以让您改进 PowerShell 脚本。
在本文中,我们将了解如何使用 PowerShell ForEach 和 ForEach-Object 并解释两者之间的区别。
ForEach() 与 ForEach-对象
PowerShell ForEach()
和 ForEach-Object
cmdlet 都允许您迭代集合并对其中的每个项目执行操作。然而,它们处理数据的方式和可用性方面存在差异。
ForEach()
cmdlet 在开始处理项目之前将整个集合加载到内存中。通过将集合加载到计算机内存中,它比 ForEach-Object
cmdlet 更快。
foreach ($fruit in $fruitsAvailable) {
Write-Host $fruit
}
另一方面,PowerShell ForEach-Object cmdlet 还可以将其通过管道传输到另一个 cmdlet 后面。这样它就可以处理管道中的每个项目。这也意味着您可以在其后面通过管道传输其他 cmdlet。
$fruitsAvailable | ForEach-Object {
Write-Host $_
}
除了这两个 cmdlet 之外,我们还有两个别名 ForEach
(注意,没有括号 ()
)和 %
。两者都是 ForEach-Object
cmdlet 的别名,但这有时可能会有点令人困惑。下面的命令都是一样的:
$fruitsAvailable | ForEach-Object {
Write-Host $_
}
$fruitsAvailable | ForEach {
Write-Host $_
}
$fruitsAvailable | % {
Write-Host $_
}
现在,对于小型集合,性能差异和内存使用量很小。在上面的示例中,ForEach()
语句仅快了 4 毫秒,并且脚本块更易于阅读。但在处理大型集合时,您可能需要考虑内存使用情况。
例如,当您加载大型 CSV 文件时,在其后面通过管道传输 ForEach-Object
cmdlet 比首先将结果存储到变量中更有效:
Import-Csv -Delimiter ";" -Path c:\temp\EmailAddresses.csv | Foreach-Object {Get-ADUser -Identity $_.Name}
PowerShell ForEach
PowerShell 中的 ForEach 语句允许您迭代数组或项目列表。 ForEach 语句的语法非常简单:
foreach ($item in $collection) {
# Script to execute with $item
}
正如您所看到的,我们可以指定要循环的集合,这可以是数组或列表,并且我们为迭代器指定一个变量。这将包含集合内每个项目的值。
例如,要将每个水果打印到控制台,我们可以执行以下操作:
$fruitsAvailable = @("Apple", "Banana", "Orange", "Grapes", "Mango", "Strawberry", "Pineapple")
foreach ($fruit in $fruitsAvailable) {
Write-Host $fruit
}
使用集合
我们还可以将 PowerShell ForEach
语句与返回集合的 cmdlet 结合使用。大多数人都会在 cmdlet 后面通过管道传输 ForEach-Object
cmdlet,但这并不总是必要的。使用 ForEach 语句的优点是与 ForEach-Object cmdlet 相比更易于阅读。
例如,Get-ChildItem
cmdlet 返回给定路径中的所有文件和文件夹。要迭代文件,我们可以执行以下操作:
foreach ($item in Get-ChildItem -path "c:\temp") {
Write-Host $item.Name
}
正如您所看到的,Get-ChildItem 返回文件或文件夹对象。在脚本块内,我们可以访问项目的所有属性,在本例中为项目名称。
中断并继续
在 PowerShell 中使用 ForEach 循环时,您有时希望在找到某个值时停止迭代或根据其值跳过某个项目。为此,我们可以使用 Break
和 Continue
关键字。
例如,如果我们想检查一个值是否可以被 7 整除。如果不是,那么我们继续下一项:
foreach ($i in 1..100) {
if ($i % 7 -ne 0 ) { continue }
Write-Host "$i is a multiple of 7"
}
Break
关键字允许您在满足特定条件时停止 PowerShell foreach 循环。现在我不经常使用这些,大多数时候您可以通过使用 where-object
cmdlet 或使用适当的过滤器来编写更好的代码。但下面的示例可让您了解如何使用 Break 关键字:
foreach ($number in 1..10) {
if ($number -gt 5) {
Write-Host "Exiting the loop because $number is greater than 5"
break
}
Write-Host "Processing number: $number"
}
嵌套 ForEach 循环
在脚本块内,您也可以使用其他 foreach 语句。尽量将其保持在最低限度,因为当您使用太多嵌套的 foreach 循环时,您的代码将变得更难以阅读和调试。
拿下面的例子来说,我们有一组球队,每个球队内部都有一组球员。外部 foreach 循环迭代 $teams
数组中的每个团队。然后,内部 foreach 循环会迭代每个团队内的球员。
# Define an array of teams, where each team has an array of players
$teams = @(
@{
TeamName = "Team A"
Players = @("Player1A", "Player2A", "Player3A")
},
@{
TeamName = "Team B"
Players = @("Player1B", "Player2B", "Player3B")
},
@{
TeamName = "Team C"
Players = @("Player1C", "Player2C", "Player3C")
}
)
# Nested foreach loop to iterate over teams and players
foreach ($team in $teams) {
Write-Host "Team: $($team.TeamName)"
foreach ($player in $team.Players) {
Write-Host " Player: $player"
}
}
PowerShell ForEach-对象
当您想要迭代从管道流式传输的项目时,可以使用 PowerShell ForEach-Object cmdlet。 ForEach-Object
的优点是,它会在项目沿着管道传输时开始对其进行处理,并且您还可以在其后面通过管道传输其他 cmdlet。
当您处理大型集合或数据集时,您需要使用此 cmdlet,因为尽管它比 foreach
语句慢,但它不会消耗那么多内存。
ForEach-Object 的另一个优点是,您可以通过使用 PowerShell 7 中的并行开关来加快速度。这允许您同时处理多个项目,稍后会详细介绍。
如前所述,ForEach-Object
cmdlet 通过管道传输到另一个 cmdlet 后面。您可以使用一个(或多个)脚本块来指定要对当前项执行的操作。例如,要获取正在运行的进程的所有进程名称,您可以执行以下操作:
Get-Process | ForEach-Object {$_.ProcessName}
正如您在上面的示例中看到的,我们使用 $_
变量引用当前项。
您可以在 PowerShell ForEach-Object 脚本块后面通过管道传输其他 cmdlet。因此,如果您想将每个进程名称存储在文本文件中,您可以执行以下操作:
Get-Process | ForEach-Object {$_.ProcessName} | Out-File "c:\temp\runningprocs.txt"
在上面的示例中,我们仅执行脚本块中的一个小脚本,这允许我们将所有内容写在一行中。但您也可以在脚本块内添加更大的脚本:
$files = Get-ChildItem -Path "C:\temp\" -File
$files | ForEach-Object {
$fileName = $_.Name
$fileSizeKB = [math]::Round($_.Length / 1KB, 2)
Write-Host "Processing file: $fileName"
Write-Host "File size: $fileSizeKB KB"
Write-Host "Processing complete for $fileName"
}
开始、结束和多个脚本块
使用 ForEach-Object cmdlet 时,可以使用 begin
和 end
脚本块。这些脚本块仅执行一次,而普通脚本块(进程脚本块)针对集合中的每个项目执行。
当您需要注册脚本的开始和结束时间,或者当您想要注册进程的开始或结束时,开始和结束脚本块非常有用:
1..5 | ForEach-Object -Begin {
Write-Host "Starting the processing..."
} -Process {
$squared = $_ * $_
Write-Host "Number: $_, Squared: $squared"
} -End {
Write-Host "Processing complete."
}
在上面的示例中,我们已经标记了每个脚本块的内容:开始块、过程块和结束块。但您不需要指定这一点(我建议这样做,因为它使您的代码更具可读性)。
如果指定多个脚本块,则第一个块始终被视为开始块。
1..2 | ForEach-Object { 'Begin Block' } { 'Process Block' }
# Result
Begin Block
Process Block
Process Block
如果有两个以上的块,则最后一个始终被视为结束块。中间的每个块都是一个进程块。
1..2 | ForEach-Object { 'Begin' } { 'Process A Block' } { 'Process B Block' } { 'End' }
# Result
Begin
Process A Block
Process B Block
Process A Block
Process B Block
End
如果您想运行多个进程块,但不想使用 begin 或 end 块,那么您必须指定参数 begin、process 和 end,并将 $null
值映射到第一个和第二个:
1..2 | ForEach-Object -Begin $null -Process { 'Process A Block' } { 'Process B Block' } -End $null
# Result
Process A Block
Process B Block
Process A Block
Process B Block
使用并行处理
如果您的脚本块非常慢或者需要处理大量项目 (10.000+),那么您可以在 PowerShell 7.x 及更高版本中使用 -parallel
参数。这样,脚本块将为每个项目并行运行,从而允许您同时处理多个项目。
默认情况下,并行参数将以 5 个为一批处理项目。但是使用 -Trottelimit
参数,我们可以定义要使用的运行空间的数量。下面的示例将按 4 个批次处理 8 个项目:
1..8 | ForEach-Object -Parallel {
Write-Host "Slow script to process $_"
Start-Sleep 1
} -ThrottleLimit 4
使用并行参数,将为每个批次创建一个新的运行空间。这意味着如果您需要引用脚本块外部的变量,则需要使用 $using:
变量来引用它:
$Message = "Slow script to process:"
1..8 | ForEach-Object -Parallel {
Write-Host $using:Message $_
Start-Sleep 1
} -ThrottleLimit 4
现在要知道的是,并行运行脚本并不总是更快。老实说,98% 的时间并没有更快。并行执行脚本块的问题在于创建运行空间来执行脚本需要时间。
如果没有并行函数,脚本块将在当前的 PowerShell 线程中执行。这样它就可以访问所有变量、管道和加载的内存。但要并行执行脚本块,需要启动一个新的 PowerShell 线程(创建新的运行空间),这需要时间(大约最多 1 秒)。
因此,只有当您的脚本块非常慢、CPU 密集型或者需要等待 API 响应(例如)时,这才有意义。
例如,编写“Hello World”只需要几毫秒:
Measure-Command { Write-Host "Hello World" } | Select TotalMilliseconds
# Result
TotalMilliseconds
-----------------
4,65
但并行执行时,执行起来几乎要30多毫秒:
Measure-Command { 1 | ForEach-Object -Parallel { Write-Host "Hello World" } } | Select TotalMilliseconds
# Result
TotalMilliseconds
-----------------
31,36
然而,如果我们有一个脚本需要超过 1 秒才能执行(这里用 start-sleep 进行模拟),那么并行方法将具有优势:
Measure-Command { 1..5 | ForEach-Object { Start-Sleep -Seconds 1 } } | Select TotalMilliseconds
# Result
TotalMilliseconds
-----------------
5054,55
# Execute in parallel, with the 5 default threads:
Measure-Command { 1..5 | ForEach-Object -Parallel { Start-Sleep -Seconds 1 } } | Select TotalMilliseconds
# Result
TotalMilliseconds
-----------------
1082,24
中断、继续和返回
就像 foreach 语句一样,我们可以使用 Break
来停止 PowerShell ForEach-Object 循环,但我们不能使用 Continue
。中断将完全停止 ForEach-Object 循环中的迭代并退出循环:
1..10 | ForEach-Object {
if ($_ -gt 5) {
Write-Host "Exiting the loop because $_ is greater than 5"
break
}
Write-Host "Processing number: $_"
}
但是,如果我们尝试在 ForEach-Object 循环内使用 Continue
,那么您将看到它也停止并存在于循环中。所以下面的例子是行不通的:
# This won't work
1..100 | ForEach-Object {
if ($_ % 7 -ne 0 ) { continue }
Write-Host "$_ is a multiple of 7"
}
相反,我们需要使用 return
关键字继续查看集合中的下一项。
1..100 | ForEach-Object {
if ($_ % 7 -ne 0 ) { return}
Write-Host "$_ is a multiple of 7"
}
总结
在大多数情况下,使用 foreach()
语句是使用 ForEach 循环的最快且最易读的方法。但是,如果您需要处理大量数据,或者需要在其后面通过管道传输其他 cmdlet,那么最好使用 ForEach-Object
cmdlet。
请记住,并行函数可能看起来很好用,但在大多数情况下,它会比较慢,因为启动新的运行空间需要时间。所以一定要衡量差异。
希望您喜欢这篇文章,如果您有任何疑问,请在下面发表评论。如果您想阅读更多这些文章,请订阅时事通讯!
猜你还喜欢
- 03-30 [玩转系统] 如何用批处理实现关机,注销,重启和锁定计算机
- 02-14 [系统故障] Win10下报错:该文件没有与之关联的应用来执行该操作
- 01-07 [系统问题] Win10--解决锁屏后会断网的问题
- 01-02 [系统技巧] Windows系统如何关闭防火墙保姆式教程,超详细
- 12-15 [玩转系统] 如何在 Windows 10 和 11 上允许多个 RDP 会话
- 12-15 [玩转系统] 查找 Exchange/Microsoft 365 中不活动(未使用)的通讯组列表
- 12-15 [玩转系统] 如何在 Windows 上安装远程服务器管理工具 (RSAT)
- 12-15 [玩转系统] 如何在 Windows 上重置组策略设置
- 12-15 [玩转系统] 如何获取计算机上的本地管理员列表?
- 12-15 [玩转系统] 在 Visual Studio Code 中连接到 MS SQL Server 数据库
- 12-15 [玩转系统] 如何降级 Windows Server 版本或许可证
- 12-15 [玩转系统] 如何允许非管理员用户在 Windows 中启动/停止服务
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[影视] 黑道中人 Alto Knights(2025)剧情 犯罪 历史 电影
[古装剧] [七侠五义][全75集][WEB-MP4/76G][国语无字][1080P][焦恩俊经典]
[实用软件] 虚拟手机号 电话 验证码 注册
[电视剧] 安眠书店/你 第五季 You Season 5 (2025) 【全10集】
[电视剧] 棋士(2025) 4K 1080P【全22集】悬疑 犯罪 王宝强 陈明昊
[软件合集] 25年6月5日 精选软件22个
[软件合集] 25年6月4日 精选软件36个
[短剧] 2025年06月04日 精选+付费短剧推荐33部
[短剧] 2025年06月03日 精选+付费短剧推荐25部
[软件合集] 25年6月3日 精选软件44个
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电视剧] 欢乐颂.5部全 (2016-2024)
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[影视] 【稀有资源】香港老片 艺坛照妖镜之96应召名册 (1996)
[剧集] 神经风云(2023)(完结).4K
[剧集] [BT] [TVB] [黑夜彩虹(2003)] [全21集] [粤语中字] [TV-RMVB]
[实用软件] 虚拟手机号 电话 验证码 注册
[资源] B站充电视频合集,包含多位重量级up主,全是大佬真金白银买来的~【99GB】
[影视] 内地绝版高清录像带 [mpg]
[书籍] 古今奇书禁书三教九流资料大合集 猎奇必备珍藏资源PDF版 1.14G
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电影] 美国队长4 4K原盘REMUX 杜比视界 内封简繁英双语字幕 49G
[电影] 死神来了(1-6)大合集!
[软件合集] 25年05月13日 精选软件16个
[精品软件] 25年05月15日 精选软件18个
[绝版资源] 南与北 第1-2季 合集 North and South (1985) /美国/豆瓣: 8.8[1080P][中文字幕]
[软件] 25年05月14日 精选软件57个
[短剧] 2025年05月14日 精选+付费短剧推荐39部
[短剧] 2025年05月15日 精选+付费短剧推荐36部
- 最新评论
-
- 热门tag