PowerShell:使用InputObject的ForEach-Object有什么意义?

时间:2019-10-28 10:31:08

标签: powershell foreach

documentation for ForEach-object说:“当将InputObject参数与ForEach-Object一起使用时,ForEach-Object值被视为一个对象。”这种行为很容易直接观察到:

InputObject

这似乎很奇怪。如果没有“每个”要做的“ ForEach”有什么意义?真的没有办法让PS C:\WINDOWS\system32> ForEach-Object -InputObject @(1, 2, 3) {write-host $_} 1 2 3 直接作用于数组的各个元素而无需管道吗?如果不是,则ForEach-objectForEach-Object似乎完全没有用。我对此不了解吗?

2 个答案:

答案 0 :(得分:2)

Bender the Greatest's helpful answer很好地解释了当前行为。

对于绝大多数cmdlet,使用-InputObject参数的直接确实毫无意义,并且应将参数视为实现细节< / em>的唯一目的是促进管道输入。

还有例外 ,例如Get-Member cmdlet,直接使用
-InputObject可以检查集合类型本身,而通过管道
提供该集合将报告有关其元素的类型的信息。

鉴于当前的工作方式,很遗憾,-InputObject在大多数cmdlet的帮助主题中具有突出的功能以及“真实”参数,并且没有用足够清晰(在撰写本文时):描述中应清楚地传达以下信息:“不要直接使用此参数,而应使用管道”。

This GitHub issue提供了哪些cmdlet如何直接处理
-InputObject参数
的分类概述。


退后一步:

从技术上讲,这是一项重大更改,对于-InputObject参数(或任何管道绑定参数)来说,默认情况下接受并枚举Line 11: Typo in component lifecycle method declaration react/no-typos参数(即使它们是通过直接参数 传递,而不是通过管道传递,方式对实现命令是透明的。

这会将直接参数输入与管道输入相提并论,前者的额外好处是可以更快地处理已经在内存中的集合。

答案 1 :(得分:1)

对于ForEach-Object或任何旨在对集合进行操作的cmdlet,将-InputObject用作直接参数是没有意义的,因为该cmdlet旨在对集合进行操作,需要一次展开并处理一个元素。但是,我也不会将参数称为“无用的”,因为它仍然需要定义,因此可以将其设置为允许通过管道输入。

为什么要这样?

-InputObject is, by convention, a generic parameter name for what should be considered to be pipeline input.这是一个设置了[Parameter(ValueFromPipeline = $true)]的参数,因此更适合从管道中获取输入,而不是作为直接参数传递。将其作为直接参数传递的主要缺点是the collection is not guaranteed to be unwrapped,并且可能表现出某些其他不良行为。从上面链接到的about_pipelines页面上:

  

将多个对象通过管道传递给命令时, PowerShell一次将对象发送给命令。使用命令参数时,对象将作为单个数组对象发送。这种微小的差异会带来重大后果。

要用不同的词来解释以上引用,通过管道传递集合(例如数组或列表)将自动展开该集合,并将其一次一次传递给管道中的下一个命令。 该cmdlet本身不会展开-InputObject ,数据一次只能传递一个元素。这就是为什么在将集合直接传递到-InputObject参数时可能会看到问题的原因-因为该cmdlet可能不是设计用来展开集合本身的,,它希望将每个集合元素传递给它是零散的。

请考虑以下示例:

# Array of hashes with a common key
$myHash = @{name = 'Alex'}, @{name='Bob'}, @{name = 'Sarah'}

# This works as intended
$myHash | Where-Object { $_.name -match 'alex' }

上面的代码按预期输出以下内容:

Name                           Value
----                           -----
name                           Alex

但是,如果您像这样直接将哈希作为InputArgument传递:

Where-Object -InputObject $myHash { $_.name -match 'alex' }

它返回整个集合,因为-InputObject从未通过管道传递时一直处于展开状态,但是在这种情况下,$_.name -match 'alex'仍返回true。换句话说,在将集合作为-InputObject的直接参数提供时, 将其视为单个对象,而不是每次对集合中的每个元素执行一次 。当针对该数据集检查错误条件时,这也可以使外观正常工作:

Where-Object -InputObject $myHash { $_.name -match 'frodo' }

最终什么也不返回,因为即使在这种情况下,frodo也不是哈希集合中任何name键的值。

简而言之,如果某些人希望输入作为管道输入传递,那么这样做通常是一个更安全的选择,即使并非总是如此,尤其是在传递集合时。但是,如果您使用的是非集合,那么选择直接使用-InputObject参数就不会有问题。