当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] ForEach-对象 (Microsoft.PowerShell.Core)

作者:精品下载站 日期:2024-12-14 02:16:15 浏览:14 分类:玩电脑

ForEach-对象 (Microsoft.PowerShell.Core)


ForEach-Object

模块 :Microsoft.PowerShell.Core

对输入对象集合中的每个项目执行操作。

句法

ForEach-Object
            [-InputObject <PSObject>]
            [-Begin <ScriptBlock>]
            [-Process] <ScriptBlock[]>
            [-End <ScriptBlock>]
            [-RemainingScripts <ScriptBlock[]>]
            [-WhatIf]
            [-Confirm]
            [<CommonParameters>]
ForEach-Object
            [-InputObject <PSObject>]
            [-MemberName] <String>
            [-ArgumentList <Object[]>]
            [-WhatIf]
            [-Confirm]
            [<CommonParameters>]
ForEach-Object
            -Parallel <scriptblock>
            [-InputObject <psobject>]
            [-ThrottleLimit <int>]
            [-TimeoutSeconds <int>]
            [-AsJob]
            [-UseNewRunspace]
            [-WhatIf]
            [-Confirm]
            [<CommonParameters>]

描述

ForEach-Object cmdlet 对输入对象集合中的每个项目执行操作。输入对象可以通过管道传输到 cmdlet 或使用 InputObject 参数指定。

从 Windows PowerShell 3.0 开始,有两种不同的方法来构造 ForEach-Object 命令。

  • 脚本块。您可以使用脚本块来指定操作。在脚本块中,使用 $_ 变量表示当前对象。脚本块是Process参数的值。脚本块可以包含任何 PowerShell 脚本。

    例如,以下命令获取计算机上每个进程的 ProcessName 属性的值。

    Get-Process | ForEach-Object {$_.ProcessName}

    ForEach-Object 支持 beginprocessend 块,如 about_functions 中所述。

    笔记

    脚本块在调用者的范围内运行。因此,这些块可以访问该范围内的变量,并且可以创建在 cmdlet 完成后保留在该范围内的新变量。

  • 操作语句。您还可以编写操作语句,这更像自然语言。您可以使用操作语句来指定属性值或调用方法。操作语句是在 Windows PowerShell 3.0 中引入的。

    例如,以下命令还获取计算机上每个进程的 ProcessName 属性的值。

    Get-Process | ForEach-Object ProcessName
  • 并行运行脚本块。从 PowerShell 7.0 开始,可以使用第三个参数集来并行运行每个脚本块。 ThrottleLimit 参数限制一次运行的并行脚本的数量。和以前一样,使用 $_ 变量来表示脚本块中的当前输入对象。使用 $using: 关键字将变量引用传递给正在运行的脚本。

    在 PowerShell 7 中,为每个循环迭代创建一个新的运行空间,以确保最大程度的隔离。如果您正在做的工作与创建新的运行空间相比较小,或者如果有大量迭代执行重要工作,这可能会对性能和资源造成很大影响。从 PowerShell 7.1 开始,默认情况下会重用运行空间池中的运行空间。 ThrottleLimit 参数设置运行空间池大小。默认运行空间池大小为 5。您仍然可以使用 UseNewRunspace 开关为每次迭代创建新的运行空间。

    默认情况下,并行脚本块使用启动并行任务的调用者的当前工作目录。

    有关详细信息,请参阅本文的注释部分。

示例

示例 1:对数组中的整数进行除法

此示例采用一个由三个整数组成的数组,并将其中每个整数除以 1024。

30000, 56798, 12432 | ForEach-Object -Process {$_/1024}

29.296875
55.466796875
12.140625

示例2:获取目录中所有文件的长度

此示例处理 PowerShell 安装目录 $PSHOME 中的文件和目录。

Get-ChildItem $PSHOME |
  ForEach-Object -Process {if (!$_.PSIsContainer) {$_.Name; $_.Length / 1024; " " }}

如果对象不是目录,脚本块将获取文件的名称,将其 Length 属性的值除以 1024,并添加一个空格 (" ") 将其与下一个文件分隔开。入口。该 cmdlet 使用 PSISContainer 属性来确定对象是否是目录。

示例 3:对最近的系统事件进行操作

此示例将系统事件日志中的 1000 个最新事件写入文本文件。在处理事件之前和之后显示当前时间。

Get-EventLog -LogName System -Newest 1000 |
    ForEach-Object -Begin {Get-Date} -Process {
        Out-File -FilePath Events.txt -Append -InputObject $_.Message
    } -End {Get-Date}

Get-EventLog 从系统事件日志中获取 1000 个最新事件,并将它们通过管道传输到 ForEach-Object cmdlet。 Begin 参数显示当前日期和时间。接下来,Process 参数使用 Out-File cmdlet 创建一个名为 events.txt 的文本文件,并将每个事件的消息属性存储在该文件中。最后,End参数用于显示所有处理完成后的日期和时间。

示例 4:更改注册表项的值

此示例将 HKCU:\Network 项下所有子项中的 RemotePath 注册表项的值更改为大写文本。

Get-ItemProperty -Path HKCU:\Network\* |
  ForEach-Object {
    Set-ItemProperty -Path $_.PSPath -Name RemotePath -Value $_.RemotePath.ToUpper()
  }

您可以使用此格式更改注册表项值的形式或内容。

网络项中的每个子项代表一个在登录时重新连接的映射网络驱动器。 RemotePath 条目包含所连接驱动器的 UNC 路径。例如,如果将 E: 驱动器映射到 \Server\Share,则会在 HKCU:\ 中创建 E 子项NetworkRemotePath 注册表值设置为 \Server\Share

该命令使用 Get-ItemProperty cmdlet 获取 Network 项的所有子项,并使用 Set-ItemProperty cmdlet 更改每个注册表项中的 RemotePath 注册表项。在Set-ItemProperty 命令中,路径是注册表项的PSPath 属性的值。这是 Microsoft .NET Framework 对象的属性,代表注册表项,而不是注册表项。该命令使用 RemotePath 值的 ToUpper() 方法,该值是一个字符串 REG_SZ

由于 Set-ItemProperty 正在更改每个键的属性,因此需要 ForEach-Object cmdlet 来访问该属性。

示例 5:使用 $null 自动变量

此示例显示将 $null 自动变量通过管道传输到 ForEach-Object cmdlet 的效果。

1, 2, $null, 4 | ForEach-Object {"Hello"}

Hello
Hello
Hello
Hello

由于 PowerShell 将 $null 视为显式占位符,因此 ForEach-Object cmdlet 会为 $null 生成一个值,就像为通过管道传递到的其他对象生成的值一样它。

示例 6:获取属性值

此示例使用 ForEach-Object cmdlet 的 MemberName 参数获取所有已安装 PowerShell 模块的 Path 属性值。

Get-Module -ListAvailable | ForEach-Object -MemberName Path
Get-Module -ListAvailable | Foreach Path

第二个命令与第一个命令等效。它使用 ForEach-Object cmdlet 的 Foreach 别名,并省略 MemberName 参数的名称(可选)。

ForEach-Object cmdlet 对于获取属性值非常有用,因为它在不更改类型的情况下获取值,这与 Format cmdlet 或 Select-Object cmdlet,它更改属性值类型。

示例 7:将模块名称拆分为组件名称

此示例显示了将两个点分隔的模块名称拆分为其组件名称的三种方法。这些命令调用字符串的Split方法。这三个命令使用不同的语法,但它们是等效的并且可以互换。所有三种情况的输出都是相同的。

"Microsoft.PowerShell.Core", "Microsoft.PowerShell.Host" |
    ForEach-Object {$_.Split(".")}
"Microsoft.PowerShell.Core", "Microsoft.PowerShell.Host" |
    ForEach-Object -MemberName Split -ArgumentList "."
"Microsoft.PowerShell.Core", "Microsoft.PowerShell.Host" |
    Foreach Split "."

Microsoft
PowerShell
Core
Microsoft
PowerShell
Host

第一个命令使用传统语法,其中包括脚本块和当前对象运算符 $_。它使用点语法来指定方法和括号来括住分隔符参数。

第二个命令使用 MemberName 参数指定 Split 方法,并使用 ArgumentList 参数来标识点 (. ) 作为分割分隔符。

第三条命令使用 ForEach-Object cmdlet 的 Foreach 别名,并省略 MemberNameArgumentList 的名称参数,可选。

示例 8:将 ForEach-Object 与两个脚本块一起使用

在此示例中,我们按位置传递两个脚本块。所有脚本块都绑定到 Process 参数。但是,它们被视为已传递给 BeginProcess 参数。

1..2 | ForEach-Object { 'begin' } { 'process' }

begin
process
process

示例 9:将 ForEach-Object 与两个以上脚本块一起使用

在此示例中,我们按位置传递四个脚本块。所有脚本块都绑定到 Process 参数。但是,它们被视为已传递给 BeginProcessEnd 参数。

1..2 | ForEach-Object { 'begin' } { 'process A' }  { 'process B' } { 'end' }

begin
process A
process B
process A
process B
end

笔记

第一个脚本块始终映射到 begin 块,最后一个脚本块映射到 end 块,中间两个块映射到 process 块。

示例 10:为每个管道项运行多个脚本块

如前面的示例所示,使用 Process 参数传递的多个脚本块将映射到 BeginEnd 参数。要避免这种映射,您必须为 BeginEnd 参数提供明确的值。

1..2 | ForEach-Object -Begin $null -Process { 'one' }, { 'two' }, { 'three' } -End $null

one
two
three
one
two
three

示例 11:并行批处理运行慢速脚本

此示例运行一个脚本块,该脚本块评估字符串并休眠一秒钟。

$Message = "Output:"

1..8 | ForEach-Object -Parallel {
    "$using:Message $_"
    Start-Sleep 1
} -ThrottleLimit 4

Output: 1
Output: 2
Output: 3
Output: 4
Output: 5
Output: 6
Output: 7
Output: 8

ThrottleLimit 参数值设置为 4,以便以四批为一组处理输入。 $using: 关键字用于将 $Message 变量传递到每个并行脚本块中。

示例 12:并行检索日志条目

此示例从本地 Windows 计算机上的 5 个系统日志中检索 50,000 个日志条目。

$logNames = 'Security', 'Application', 'System', 'Windows PowerShell',
    'Microsoft-Windows-Store/Operational'

$logEntries = $logNames | ForEach-Object -Parallel {
    Get-WinEvent -LogName $_ -MaxEvents 10000
} -ThrottleLimit 5

$logEntries.Count

50000

Parallel 参数指定为每个输入日志名称并行运行的脚本块。 ThrottleLimit 参数可确保所有五个脚本块同时运行。

示例 13:作为作业并行运行

此示例创建一个并行运行脚本块的作业,一次两个。

PS> $job = 1..10 | ForEach-Object -Parallel {
    "Output: $_"
    Start-Sleep 1
} -ThrottleLimit 2 -AsJob

PS> $job

Id     Name            PSJobTypeName   State         HasMoreData     Location      Command
--     ----            -------------   -----         -----------     --------      -------
23     Job23           PSTaskJob       Running       True            PowerShell    …

PS> $job.ChildJobs

Id     Name            PSJobTypeName   State         HasMoreData     Location      Command
--     ----            -------------   -----         -----------     --------      -------
24     Job24           PSTaskChildJob  Completed     True            PowerShell    …
25     Job25           PSTaskChildJob  Completed     True            PowerShell    …
26     Job26           PSTaskChildJob  Running       True            PowerShell    …
27     Job27           PSTaskChildJob  Running       True            PowerShell    …
28     Job28           PSTaskChildJob  NotStarted    False           PowerShell    …
29     Job29           PSTaskChildJob  NotStarted    False           PowerShell    …
30     Job30           PSTaskChildJob  NotStarted    False           PowerShell    …
31     Job31           PSTaskChildJob  NotStarted    False           PowerShell    …
32     Job32           PSTaskChildJob  NotStarted    False           PowerShell    …
33     Job33           PSTaskChildJob  NotStarted    False           PowerShell    …

ThrottleLimit 参数限制一次运行的并行脚本块的数量。 AsJob 参数使 ForEach-Object cmdlet 返回作业对象,而不是将输出流式传输到控制台。 $job 变量接收收集输出数据并监视运行状态的作业对象。 $job.ChildJobs 属性包含运行并行脚本块的子作业。

示例 14:使用线程安全变量引用

此示例并行调用脚本块来收集唯一命名的 Process 对象。

$threadSafeDictionary = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new()
Get-Process | ForEach-Object -Parallel {
    $dict = $using:threadSafeDictionary
    $dict.TryAdd($_.ProcessName, $_)
}

$threadSafeDictionary["pwsh"]

NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     82    82.87     130.85      15.55    2808   2 pwsh

ConcurrentDictionary 对象的单个实例被传递到每个脚本块以收集对象。由于ConcurrentDictionary是线程安全的,因此每个并行脚本对其进行修改都是安全的。非线程安全对象,例如 System.Collections.Generic.Dictionary,在此处使用是不安全的。

笔记

此示例是 Parallel 参数的低效使用。该脚本将输入对象添加到并发字典对象中。这是微不足道的,不值得在单独的线程中调用每个脚本的开销。在不使用 Parallel 开关的情况下运行 ForEach-Object 会更高效、更快。这个例子只是为了演示如何使用线程安全变量。

示例 15:并行执行时写入错误

此示例并行写入错误流,其中写入错误的顺序是随机的。

1..3 | ForEach-Object -Parallel {
    Write-Error "Error: $_"
}

Write-Error: Error: 1
Write-Error: Error: 3
Write-Error: Error: 2

示例 16:终止并行执行中的错误

此示例演示了一个并行运行的脚本块中的终止错误。

1..5 | ForEach-Object -Parallel {
    if ($_ -eq 3)
    {
        throw "Terminating Error: $_"
    }

    Write-Output "Output: $_"
}

Exception: Terminating Error: 3
Output: 1
Output: 4
Output: 2
Output: 5

Output: 3 从未被写入,因为该迭代的并行脚本块已终止。

笔记

即使使用 $using: 关键字,在 Foreach-Object -Parallel 场景中也不支持 PipelineVariable 公共参数变量。

示例 17:在嵌套并行脚本 ScriptBlockSet 中传递变量

您可以在 Foreach-Object -Parallel 作用域脚本块外部创建变量,并在脚本块内部通过 $using 关键字使用它。

$test1 = 'TestA'
1..2 | Foreach-Object -Parallel {
    $using:test1
}

TestA
TestA

# You CANNOT create a variable inside a scoped scriptblock
# to be used in a nested foreach parallel scriptblock.
$test1 = 'TestA'
1..2 | Foreach-Object -Parallel {
    $using:test1
    $test2 = 'TestB'
    1..2 | Foreach-Object -Parallel {
        $using:test2
    }
}

Line |
   2 |  1..2 | Foreach-Object -Parallel {
     |         ~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The value of the using variable '$using:test2' can't be retrieved because it has
     | not been set in the local session.

嵌套脚本块无法访问 $test2 变量并引发错误。

示例 18:创建并行运行脚本的多个作业

ThrottleLimit 参数限制每个 ForEach-Object -Parallel 实例期间运行的并行脚本的数量。它不限制使用 AsJob 参数时可以创建的作业数量。由于作业本身同时运行,因此可以创建多个并行作业,每个作业最多运行到并发脚本块的限制数量。

$jobs = for ($i=0; $i -lt 10; $i++) {
    1..10 | ForEach-Object -Parallel {
        ./RunMyScript.ps1
    } -AsJob -ThrottleLimit 5
}

$jobs | Receive-Job -Wait

此示例创建 10 个正在运行的作业。每个作业同时运行的脚本不超过 5 个。同时运行的实例总数限制为 50 个(10 个作业乘以 ThrottleLimit 5)。

参数

-ArgumentList

指定方法调用的参数数组。有关 ArgumentList 行为的更多信息,请参阅 about_Splatting。

此参数是在 Windows PowerShell 3.0 中引入的。

类型 :

Object[]

别名:

Args

位置:

命名

默认值:

None

必需的:

False

接受管道输入:

False

接受通配符:

False

-AsJob

导致并行调用作为 PowerShell 作业运行。返回单个作业对象,而不是正在运行的脚本块的输出。作业对象包含每个运行的并行脚本块的子作业。您可以将作业对象与任何 PowerShell 作业 cmdlet 结合使用来查看运行状态并检索数据。

此参数是在 PowerShell 7.0 中引入的。

类型 :

SwitchParameter

位置:

命名

默认值:

None

必需的:

False

接受管道输入:

False

接受通配符:

False

-Begin

指定在此 cmdlet 处理任何输入对象之前运行的脚本块。该脚本块在整个管道中仅运行一次。有关 begin 块的更多信息,请参阅 about_Functions。

类型 :

ScriptBlock

位置:

命名

默认值:

None

必需的:

False

接受管道输入:

False

接受通配符:

False

-Confirm

在运行 cmdlet 之前提示您进行确认。

类型 :

SwitchParameter

别名:

cf

位置:

命名

默认值:

False

必需的:

False

接受管道输入:

False

接受通配符:

False

-End

指定在此 cmdlet 处理所有输入对象后运行的脚本块。该脚本块在整个管道中仅运行一次。有关 end 块的更多信息,请参阅 about_Functions。

类型 :

ScriptBlock

位置:

命名

默认值:

None

必需的:

False

接受管道输入:

False

接受通配符:

False

-InputObject

指定输入对象。 ForEach-Object 在每个输入对象上运行脚本块或操作语句。输入包含对象的变量,或键入获取对象的命令或表达式。

当您将 InputObject 参数与 ForEach-Object 一起使用时,不是通过管道将命令结果传送到 ForEach-ObjectInputObject 值被视为单个对象。即使该值是命令结果的集合(例如 -InputObject (Get-Process)),情况也是如此。由于 InputObject 无法从数组或对象集合中返回单个属性,因此我们建议您使用 ForEach-Object 对这些对象的对象集合执行操作如果定义的属性具有特定值,则可以在管道中使用 ForEach-Object,如本主题中的示例所示。

类型 :

PS对象

位置:

命名

默认值:

None

必需的:

False

接受管道输入:

True

接受通配符:

False

-MemberName

指定要获取的成员属性的名称或要调用的成员方法。成员必须是实例成员,而不是静态成员。

允许使用通配符,但仅当结果字符串解析为唯一值时才有效。例如,如果您运行 Get-Process |对于Each -MemberName *Name,通配符模式匹配多个成员,导致命令失败。

此参数是在 Windows PowerShell 3.0 中引入的。

类型 :

String

位置:

0

默认值:

None

必需的:

True

接受管道输入:

False

接受通配符:

True

-Parallel

指定用于并行处理输入对象的脚本块。输入描述操作的脚本块。

此参数是在 PowerShell 7.0 中引入的。

类型 :

ScriptBlock

位置:

命名

默认值:

None

必需的:

True

接受管道输入:

False

接受通配符:

False

-Process

指定对每个输入对象执行的操作。该脚本块针对管道中的每个对象运行。有关 process 块的更多信息,请参阅 about_Functions。

当您向 Process 参数提供多个脚本块时,第一个脚本块始终映射到 begin 块。如果只有两个脚本块,则第二个脚本块将映射到 process 块。如果有三个或更多脚本块,则第一个脚本块始终映射到 begin 块,最后一个脚本块映射到 end 块,中间块映射到到 process 块。

类型 :

脚本块[]

位置:

0

默认值:

None

必需的:

True

接受管道输入:

False

接受通配符:

False

-RemainingScripts

指定 Process 参数未采用的所有脚本块。

此参数是在 Windows PowerShell 3.0 中引入的。

类型 :

脚本块[]

位置:

命名

默认值:

None

必需的:

False

接受管道输入:

False

接受通配符:

False

-ThrottleLimit

指定并行运行的脚本块的数量。输入对象将被阻止,直到运行的脚本块计数低于 ThrottleLimit。默认值为5

ThrottleLimit 参数限制每个 ForEach-Object -Parallel 实例期间运行的并行脚本的数量。它不限制使用 AsJob 参数时可以创建的作业数量。由于作业本身是并发运行的,因此可以创建多个并行作业,每个作业最多运行到并发脚本块的限制数量。

此参数是在 PowerShell 7.0 中引入的。

类型 :

整数32

位置:

命名

默认值:

5

必需的:

False

接受管道输入:

False

接受通配符:

False

-TimeoutSeconds

指定等待并行处理所有输入的秒数。在指定的超时时间后,所有正在运行的脚本将停止。并且将忽略任何剩余的要处理的输入对象。默认值 0 禁用超时,并且 ForEach-Object -Parallel 可以无限期运行。在命令行中键入 Ctrl+C 会停止正在运行的 ForEach-Object -Parallel 命令。此参数不能与 AsJob 参数一起使用。

此参数是在 PowerShell 7.0 中引入的。

类型 :

整数32

位置:

命名

默认值:

0

必需的:

False

接受管道输入:

False

接受通配符:

False

-UseNewRunspace

导致并行调用为每个循环迭代创建一个新的运行空间,而不是重用运行空间池中的运行空间。

该参数是在PowerShell 7.1中引入的

类型 :

开关参数

位置:

命名

默认值:

False

必需的:

False

接受管道输入:

False

接受通配符:

False

-WhatIf

显示 cmdlet 运行时会发生什么情况。该 cmdlet 未运行。

类型 :

SwitchParameter

别名:

wi

位置:

命名

默认值:

False

必需的:

False

接受管道输入:

False

接受通配符:

False

输入

PSObject

您可以通过管道将任何对象传递给此 cmdlet。

输出

PSObject

此 cmdlet 返回由输入确定的对象。

笔记

PowerShell 包含以下 ForEach-Object 别名:

  • 所有平台:

      %
    foreach

    ForEach-Object cmdlet 的工作方式与 Foreach 语句非常相似,只不过您无法将输入通过管道传输到 Foreach 语句。有关 Foreach 语句的详细信息,请参阅 about_Foreach。

    从 PowerShell 4.0 开始,添加了 WhereForEach 方法以与集合一起使用。您可以在这里阅读有关这些新方法的更多信息 about_arrays

    使用ForEach-Object -Parallel:

    • ForEach-Object -Parallel 在新的运行空间中运行每个脚本块。与使用顺序处理运行 ForEach-Object 相比,新的运行空间会产生更多的开销。使用并行非常重要,因为与脚本块执行的工作相比,并行运行的开销很小。例如:

      • 在多核机器上计算密集型脚本
    • 花费时间等待结果或执行文件操作的脚本

    使用并行参数可能会导致脚本运行速度比正常情况慢得多。特别是如果并行脚本很简单。尝试并行来发现它的好处。

  • 并行运行时,如果用 ScriptPropertiesScriptMethods 修饰的对象在与最初附加到它们的脚本不同的运行空间中运行,则不能保证它们正确运行。

    脚本块调用始终尝试在其home运行空间中运行,无论它实际在何处调用。但是,ForEach-Object -Parallel 会创建临时运行空间,这些运行空间在使用后会被删除,因此不再有运行空间可供脚本执行。

    只要home运行空间仍然存在,此行为就可以工作。但是,如果脚本依赖于仅存在于调用者运行空间而不是home运行空间中的外部变量,您可能无法获得所需的结果。

  • 非终止错误将在并行运行的脚本块中发生时写入 cmdlet 错误流。由于并行脚本块执行顺序是不确定的,因此错误在错误流中出现的顺序是随机的。同样,写入其他数据流的消息(例如警告、详细信息或信息)也会以不确定的顺序写入这些数据流。

    终止错误(例如异常)会终止发生错误的脚本块的各个并行实例。一个脚本块中的终止错误可能不会导致 Foreach-Object cmdlet 终止。并行运行的其他脚本块将继续运行,除非它们也遇到终止错误。终止错误作为 ErrorRecord 写入错误数据流,FullyQualifiedErrorIdPSTaskException。可以使用 PowerShell try/catchtrap 块将终止错误转换为非终止错误。

  • 即使使用 $using: 关键字,并行场景中也不支持 PipelineVariable 公共参数变量。

    这很重要

    ForEach-Object -Parallel 参数集在单独的进程线程上并行运行脚本块。 $using: 关键字允许将变量引用从 cmdlet 调用线程传递到每个正在运行的脚本块线程。由于脚本块在不同的线程中运行,因此必须安全地使用通过引用传递的对象变量。一般来说,从不改变的引用对象中读取是安全的。如果需要修改对象状态,则必须使用线程安全对象,例如 .NET System.Collection.Concurrent 类型(请参见示例 14)。

  • 您需要 登录账户 后才能发表评论

    取消回复欢迎 发表评论:

    关灯