我试图将一个字符串数组管道写入主机并显式使用$_
来编写这些字符串:
'foo', 'bar', 'baz' | write-host $_
然而,它失败了:
输入对象不能绑定到命令的任何参数,因为该命令不接受管道输入或输入及其属性与采用管道输入的任何参数都不匹配。
这个错误信息对我没有意义,因为我完全能够写
'foo', 'bar', 'baz' | write-host
我原以为两个管道都是等价的。显然,他们不是。那么,有什么区别?
我原以为两个管道都是等价的。
他们不是:
'foo', 'bar', 'baz' | write-host
它是以下基于管道的等效物(相当于最终效果,而不是技术上):
foreach ($str in 'foo', 'bar', 'baz') { Write-Host -Object $str }
也就是说,在你的命令中,Write-Host
接收来自管道的输入,该管道隐含地绑定到每个输入对象的-Object
参数,因为参数-Object
被声明为通过属性[Parameter(ValueFromPipeline=$true)]
接受管道输入
'foo', 'bar', 'baz' | write-host $_
在管道处理开始之前,在你的情况下,参数 - $_
- 首先绑定到参数:
由于$_
前面没有参数名称,因此它在位置上与 - 隐含 - -Object
参数绑定。
然后,当管道处理开始时,管道参数绑定找不到管道绑定的Write-Host
参数再绑定,因为只有这样的参数-Object
已被绑定,即通过参数$_
。
换句话说:你的命令错误地尝试将-Object
参数绑定两次;不幸的是,错误消息并没有完全清楚。
更重要的一点是,使用$_
只能在为每个输入对象计算的脚本块({ ... }
)中有意义。
在这种情况之外,$_
(或其别名,$PSItem
)通常没有价值,不应该使用。
虽然$_
最常用于传递给ForEach-Object
和Where-Object
cmdlet的脚本块,但还有另一个有用的应用程序,最常用的是Rename-Item
cmdlet:a delay-bind script-block argument:
# Example: rename *.txt files to *.dat files using a delay-bind script block:
Get-Item *.txt | Rename-Item -NewName { $_.BaseName + '.dat' }
也就是说,不是将静态新名称传递给Rename-Item
,而是传递一个为每个输入对象计算的脚本块 - 输入对象像往常一样绑定到$_
- 这将启用动态行为。
然而,正如链接答案中所解释的,这种技术仅适用于(a)管道绑定和(b)不是[object]
或[scriptblock]
类型的参数;因此,鉴于Write-Object
的-Object
参数是[object]
类型,该技术不起作用:
# Try to enclose all inputs in [...] on output.
# !! DOES NOT WORK.
'foo', 'bar', 'baz' | write-host -Object { "[$_]" }
因此,在这种情况下,基于管道的解决方案需要使用ForEach-Object
:
# -Object is optional
PS> 'foo', 'bar', 'baz' | ForEach-Object { write-host -Object "[$_]" }
[foo]
[bar]
[baz]
您可以像iRon在评论中指出的那样使用它。 $_
或$PSItem
是正在处理的管道中的当前对象。通常,您会看到需要处理或脚本块的命令。您必须在类似的处理块中包含您的Write-Host
命令。
'foo', 'bar', 'baz' | ForEach-Object {write-host $_}
以下是使用函数的过程块的示例:
function write-stuff {
process { write-host $_ }
}
'foo', 'bar', 'baz' | write-stuff
bar
foo
hi