【问题标题】:Powershell Parameter binding ByPropertyName and ByValuePowershell 参数绑定 ByPropertyName 和 ByValue
【发布时间】:2020-07-27 09:33:10
【问题描述】:

我想停止名为“ALG”的服务,所以我使用: "alg" | stop-service 有用。 Get-help stop-service -parameter name 说:管道输入:true(ByPropertyName, ByValue) 和“alg”是“ByPropertyValue”对吗? 我想停止一个名为记事本的进程,所以我使用: "notepad" | stop-process 我得到一个错误。 Get-help stop-process -parameter name 说:管道输入真(ByPropertyName)和“记事本”是“ByPropertyName”?为什么会出现这个错误? 感谢您的帮助

【问题讨论】:

  • 按属性名和按值不同。
  • "notepad" 不是 ByPropertyName,它是ByValueByValue 一词可能令人困惑,但它所说的只是:绑定输入对象作为一个整体,而不是通过它的一个属性值(即ByPropertyName )。

标签: powershell parameterbinding


【解决方案1】:

如果你想将一个对象的值绑定到一个参数通过属性名,要么:

  1. 传递具有适当命名属性的对象:
[pscustomobject]@{Name='notepad'} |Stop-Process
# or, for older versions of powershell:
'notepad' |Select @{Name='Name';Expression={$_}} |Stop-Process
  1. 将管道表达式显式绑定到命名参数:
'notepad' |Stop-Process -Name {$_}

【讨论】:

  • 这是否意味着:ByValue= 我正在提供一个字符串(如“alg”),PowerShell 找到一个属性“Name”并停止服务。 ByPropertyName= 我正在提供一个对象,它必须有一个属性“名称”?
  • 因为[string]“记事本”没有有任何Name属性
  • ByPropertyName 绑定意味着参数在输入对象中查找具有该名称的属性。 String 对象没有 Name 属性。
  • @Purclot 那是因为Stop-Service 上的Name 参数标有both - 它将接受按属性名称输入,如果失败,它会回退到按值绑定
  • @mklement0 我正在为问题的“这如何工作”部分写一个解释,但它很冗长
【解决方案2】:
  • Mathias R. Jessen's 答案提供了一个解决方案,用于将字符串(进程名称)传送到Stop-Process

  • js2010's answer 有正确的解释为什么管道字符串到Stop-Process 不起作用(无需额外的努力),并且还提供了一种有用的跟踪技术参数绑定,但是 - 在撰写本文时 - 答案包含使问题有点混淆的附带信息。

让我提供更详细的解释:


Stop-Process,与 Stop-Service 不同,不是旨在接受 字符串(进程名称)作为管道输入。

虽然字符串输入 in the abstract 仍然可以工作,即如果字符串可以自动转换为命令的ByValue 所期望的数据类型之一(整个-object) 管道绑定参数,这 不是 Stop-Process 的情况,因为进程 name(字符串)不能(自动)转换为 System.Diagnostics.Process实例 (-InputObject)[1].

  • 当 PowerShell 在调用期间考虑将管道输入绑定到参数-Name 时,它会查找具有Name 属性的对象,因为参数声明指定只有在输入对象具有为参数命名的属性时才接受管道输入

    • 在帮助主题中,例如Stop-Process 的主题,这表示为ByPropertyName

    • 在代码中,表示为System.Management.Automation.ParameterAttribute类型的布尔ValueFromPipelineByPropertyName属性;也就是说,用 PowerShell 代码表示,参数声明类似于:注意,ValueFromPipelineByPropertyNameValueFromPipelineByPropertyName = $true 的缩写
      [Parameter(ValueFromPipelineByPropertyName)] [string[]] $Name

  • [string] (System.String) 实例(例如 "alg")没有 Name 属性 - 它是本身的名称。

  • 因此,在没有自动转换[1] 到唯一ByValue 参数System.Diagnostics.Process 类型的-InputObject 的情况下,并且在缺少NameId 属性的ByPropertyValue 参数,调用 fails 并带有以下错误消息,它实质上告诉您管道输入无效(不能绑定到任何参数):

    • 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.

Stop-Service,相比之下, 被设计为接受字符串输入,因为它的-Name 参数(也)被声明为接受字符串直接 作为输入对象,作为一个整体

  • 在帮助主题中,例如Stop-Service 的主题,这表示为ByValue

  • 在PowerShell代码中,表示为ValueFromPipeline
    [Parameter(ValueFromPipeline)] [string[]] $Name

注意:

  • 虽然给定的参数可以是 both ByValue ByPropertyValue - 这确实是Stop-Service-Name 参数的情况 - 那是'不典型。

  • 通常,管道绑定参数被声明为标量而不是数组(例如,Sort-Object-InputObject <PSObject> 而不是-InputObject <PSObject[]>),这意味着只支持传递多个参数通过管道,而不是通过直接参数 - 有关背景信息,请参阅 GitHub issue #4242


检查管道绑定参数:

  • 命令的给定参数
PS> Get-Help Stop-Process -Parameter Name

-Name <String[]>
    Specifies the process names of the processes to stop. You can type multiple process names, separated by commas, or use wildcard characters.
    
    Required?                    true
    Position?                    named
    Default value                None
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  true

注意Accept pipeline input? 行;对于 -管道绑定参数,您会在第二列中看到 False

  • 给定命令支持的所有管道绑定参数
PS> Get-Help Stop-Process -Parameter * | Where pipelineInput -like True*

-Id <Int32[]>
    Specifies the process IDs of the processes to stop. To specify multiple IDs, use commas to separate the IDs. To find the PID of a process, type 
    `Get-Process`.
    
    Required?                    true
    Position?                    0
    Default value                None
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  false
    

-InputObject <Process[]>
    Specifies the process objects to stop. Enter a variable that contains the objects, or type a command or expression that gets the objects.
    
    Required?                    true
    Position?                    0
    Default value                None
    Accept pipeline input?       True (ByValue)
    Accept wildcard characters?  false
    

-Name <String[]>
    Specifies the process names of the processes to stop. You can type multiple process names, separated by commas, or use wildcard characters.
    
    Required?                    true
    Position?                    named
    Default value                None
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  false

注意:上述技术从基于 MAML 的帮助文件中收集参数信息,可能伴随给定的 cmdlet(大多数内置 cmdlet 确实带有此类帮助文件)。虽然帮助文件中的信息应该正确地反映了 cmdlet 的实际参数定义并且通常如此,但不能保证。如有疑问,请改用以下技术,直接检查 cmdlet 的实际定义:

# Lists all pipeline-binding parameters defined by the given cmdlet,
# by examining the actual cmdlet definition.
(Get-Command Stop-Process).ParameterSets.Parameters | 
  Where-Object { $_.ValueFromPipeline -or $_.ValueFromPipelineByPropertyName} |
    Select-Object -Unique Name, Aliases, ParameterType, @{
      Name = 'Accepts pipeline input'
      Expression = { 
        'True ({0})' -f  ($(
           if ($_.ValueFromPipeline) { 'ByValue'}
           if ($_.ValueFromPipelineByPropertyName) { 'ByPropertyName' }
        ) -join ', ')
      }
    } | Sort-Object Name

[1] 除非通过 System.Management.Automation.ArgumentTransformationAttribute 派生属性(不常见)声明参数支持自定义类型转换,否则此处适用 PowerShell 的常用转换规则,该规则采用多种技术,在 @987654329 中讨论@。在System.Diagnostics.Process 的情况下,不可能从字符串转换,因为目标类型既没有具有字符串的单参数构造函数,也没有静态.Parse() 方法。可转换性的快速测试是尝试 cast: [System.Diagnostics.Process] 'notepad' 失败。相比之下,[System.ServiceProcess.ServiceController] 'alg' 有效,因为该类型确实具有a single-parameter constructor that accepts a string,但请注意,此转换不会在参数绑定期间发挥作用 在对 Stop-Service 的调用中,例如 'alg' | Stop-Service - 在那里,字符串按原样绑定到 ByValue -Name 参数。

【讨论】:

    【解决方案3】:

    "stop-service -name" 可以按值(字符串)传递,所以它使用它。停止进程无法将字符串转换为 -inputobject 的进程类型,即管道按值。 "Stop-process -name" 仅由属性名称提供。

    这里还有一些细微差别。可以将字符串强制输入 servicecontroller,但不能强制输入 process。因此,理论上可以将字符串作为 -inputobject 输入以用于停止服务,但不能用于停止进程。 “stop-service -inputobject alg”也可以,但“stop-process -inputobject notepad”不能。这两个命令之间的不一致可能会令人困惑。

    [System.ServiceProcess.ServiceController]'alg'
    
    Status   Name               DisplayName
    ------   ----               -----------
    Stopped  alg                Application Layer Gateway Service
    
    
    [system.diagnostics.process]'notepad'
    InvalidArgument: Cannot convert the "notepad" value of type "System.String" to type "System.Diagnostics.Process".
    

    如果你真的想看绑定:

    trace-command parameterbinding {'alg' | stop-service} -pshost
    trace-command parameterbinding {'notepad' | stop-process} -pshost
    

    【讨论】:

    • 嗯,现在我有点困惑。保持 Start-Service(或 stop-Service):ByValue=输入是类型的“整体”对象:[-InputObject] ,因此使用:“alg”| start(stop)-Service,“alg”绝对不是类型的对象:ServiceController 也不是具有属性“name”的任何类型的对象,意味着“alg”将(必须?)转换为类型为 ServiceController 的对象.据我了解,帮助:总是首先使用 ByValue?
    • 我认为因为 -name 是字符串类型,所以'alg'首先匹配它。并且“stop-service -name”可以按值,但“stop-process -name”不能按值。
    • 感谢更新,js2010,但是关于“更多细微差别”的段落仍然具有误导性;相比之下,关于跟踪参数绑定的提示很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2013-12-24
    相关资源
    最近更新 更多