【问题标题】:Use of property names in PowerShell pipeline $_ variable在 PowerShell 管道 $_ 变量中使用属性名称
【发布时间】:2016-03-05 03:35:57
【问题描述】:

作为一名 C# 开发人员,我仍在学习 PowerShell 的基础知识并且经常感到困惑。 为什么 $_.给我第一个示例中有效属性名称的智能感知列表,而不是第二个示例?

Get-Service | Where {$_.name -Match "host" } 

Get-Service | Write-Host $_.name

这两个例子的基本区别是什么?

当我运行第二个时,它在 Get-Service 的每次迭代中都会出现此错误:

Write-Host : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters 
that take pipeline input.
At line:3 char:15
+ Get-Service | Write-Host $_.name
+               ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (wuauserv:PSObject) [Write-Host], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.WriteHostCommand

我和我的同事首先使用一个 foreach 循环来迭代一个 Get-commandlet,但我们却无法让属性名称出现。我们试图简化,直到我们进入上面的核心基础。

只是发现有时这是命令行开关中的拼写错误,下面的第一个错误是因为命令行开关应该是 Get-Service 而不是 Get-Services。

foreach ($item in Get-Services) 
 {
    Write-Host $item.xxx  #why no intellisense here? 
 }


foreach ($item in Get-Service) 
 {
    Write-Host $item.Name 
 }

【问题讨论】:

  • 仅供参考:Get-Service | Select -Expand Name 让这变得简单多了。如果您只想从一系列事物中获取一个属性的值,Select -Expand 非常适合扩展该属性的值并为每个对象返回该值。
  • 为了使 BatekB 的解释更简单,这是因为您没有通过管道连接到 for-each 循环:get-service | % { Write-host $_.Name } 将起作用
  • @Cole9350 - 你的 cmets 听起来是我的最佳答案 - 为什么不把它放在下面的答案中,我会选择它。
  • @Neal 我相信 bartek 涵盖了所有基础并在技术上进行了最佳解释

标签: powershell


【解决方案1】:

第一部分:你不能像那样使用$_,它只在脚本块中表示当前管道对象。这些脚本块通常与任何*-Object cmdlet 一起使用(但也有其他用例)。并非所有参数/ cmdlet 都支持它。 Write-Host 就是其中之一。

第二:看起来您正在使用自己的函数(GetServices)。 PowerShell v3 智能感知取决于命令元数据 (OutputType)。如果任何 cmdlet/ 函数产生对象但对 OutputType 保持沉默,则智能感知将不起作用。获取它非常简单,你可以撒谎并且仍然可以为任何现有类型获得适当的智能感知:

function Get-ServiceEx {
[OutputType('System.ServiceProcess.ServiceController')]
param ()
    'This is not really a service!'
}

(Get-ServiceEx).<list of properties of ServiceController object>

您可以阅读有关it on my blog 的更多信息。

【讨论】:

  • 哇,我从来不知道即使没有Foreach-ObjectWhere-Object,你也可以接受$_。甜蜜。
  • 当然,我最喜欢的例子是重命名项目:ls *.txt | ren -new { $_.Name -replace '\.txt', '.old' } -WhatIf
【解决方案2】:

如果您将$_ 放入脚本块中,Intellisense 将起作用。 以下将触发智能感知:

Get-Service | Foreach-Object {$_.Name} # Intellisense works
Get-Service | Where-Object {$_.Name}   # Intellisense works
Get-Service | Write-Host {$_.Name}     # Intellisense works

请注意,您的命令不必是有效命令:第三个示例将不起作用,但智能感知将显示 $_ 的自动完成,因为它位于脚本块内。

我怀疑这是因为 $_ 只能在脚本块中使用(例如 switch、%、?),但我没有确凿的证据。

【讨论】:

    【解决方案3】:

    $_ 是管道中的当前对象。与foreach ($item in $array) 中的$item 相同。

    Get-Service | Where {$_.name -Match "host" } 
    
    Get-Service | Write-Host $_.name
    

    这两行之间的区别是 PowerShell 设计的基本部分。管道应该很容易。你应该可以前。搜索和删除文件很简单:

    Get-ChildItem *.txt | Remove-Item
    

    这很干净,简单,每个人都可以使用它。 cmdlet 用于识别传入对象的类型、适应支持特定类型并处理对象。

    但是,有时您需要在管道中进行更高级的对象操作,这就是 Where-ObjectForeach-Object 的用武之地。这两个 cmdlet 都允许您编写自己的处理逻辑,而无需先创建函数或 cmdlet。为了能够访问代码中的对象(处理逻辑),您需要一个标识符,即$_$_ 在其他一些特殊的 cmdlet 中也支持,但在大多数 cmdlet 中不使用(包括Write-Host)。

    此外,foreach ($item in Get-Service) 中的 Intellisense 也可以工作。你打错字了。

    【讨论】:

    • 这篇文章的第一行没有意义。此外,“构建 cmdlet 以识别传入对象的类型、适应支持特定类型并处理对象。”不是真的。 Remove-Item 在接受字符串的默认路径参数上接受管道输入,因此它只是调用对象的 tostring() 函数。它不关心也不知道它最初是什么类型,通过添加 -recurse 参数时换行的速度来证明
    • 对不起,如果我使用了一个不好的例子,但这个想法仍然有效。管道(没有 foreach-/where-object)应该是简单和动态的。也许Remove-Item 的行为不会因对象类型而异,但关键是 cmdlet 可以而且应该,至少对于最有可能通过管道传输到其中的对象类型而言。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 2021-09-21
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-09
    相关资源
    最近更新 更多