【问题标题】:Pipeline input not being validated when a function emits no output down the pipeline for ValueFromPipelineByPropertyName parameters当函数在管道中为 ValueFromPipelineByPropertyName 参数发出任何输出时,管道输入未得到验证
【发布时间】:2022-07-07 00:00:26
【问题描述】:

我能够以更通用的方式重现此问题,但问题与最初提出的问题不同。我已经重写了这个问题,以反映遇到的问题以及一个通用的可重现示例。


我有一个 cmdlet,当它找不到任何要返回的数据时,有时它不会产生任何输出。但是,我使用此函数将信息传递给另一个 cmdlet,该 cmdlet 通过 ValueFromPipelineByPropertyName 属性接受管道输入。当有一个实际对象通过管道传递时,一切都按预期工作,包括参数验证检查。但是,如果传递的对象是$null,则跳过参数验证。请注意,当简单地将$null 传递到管道中时,这是不可重现的;我只能在管道发出无输出时重现此问题。

我已经能够概括地重现这一点。参数定义的属性和我的真实代码一样:

Function Get-InfoTest {
  Param(
    [switch]$ReturnNothing
  )

  if( !$ReturnNothing ) {
    [PSCustomObject]@{
      Name = 'Bender'
      Age = [int]::MaxValue
    }
  }
}

Function Invoke-InfoTest {
  Param(
    [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
    [string]$Name,
    [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
    [int]$Age
  )

  Write-Host "Hello, $Name. I see you are $Age years old."
}

# With valid object
Get-InfoTest | Invoke-InfoTest


# Correct behavior when $null is directly passed into the cmdlet, throws error
$null | Invoke-InfoTest

# With returned null object, should throw an error but executes with an incorrect result
Get-InfoTest -ReturnNothing | Invoke-InfoTest

这里发生了什么?虽然在函数体中编写空或空白检查并不难,但这是Mandatory 参数选项以及Validate* 参数属性的重点。在我的真实代码中,我现在需要为已经设置了验证属性的几个参数编写空或空白检查。如代码 cmets 中所述,将$null 传递到目标 cmdlet 会导致引发正确的错误,但函数不产生任何输出会导致函数执行,就好像一切都正确提供了一样。

【问题讨论】:

  • @SantiagoSquarzon 我已经用有关该问题的新细节和合适的minimal reproducible example 更新了问题正文。
  • “接收函数永远不会执行” - @SantiagoSquarzon 什么?问题是接收函数正在被执行。也许我误解了你的意思?
  • 你是对的,我的错,接收函数的end被执行但是如果你把你的Write-Host语句放在@987654331 @block 你会看到我想说的话。由于这是一个管道功能,因此无需处理任何内容
  • 啊,也许这就是解决方案?我的意思是将我的函数体放在定义的process 块中。
  • TBH 这是我的一个误解。出于某种原因,我认为函数体默认为 process 块,而不是 end

标签: powershell


【解决方案1】:

事实证明,这是由于我对函数的执行方式存在误解而导致的预期行为。如果您没有定义 begin/process/end 块,函数体默认为 end 块。但是,将函数体放在显式的 process 块中会导致正确的行为,因为 process 块是您通常希望执行代码的地方。

Invoke-InfoTest 的以下修改导致示例代码在所有情况下都能正常工作:

Function Invoke-InfoTest {
  Param(
    [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
    [string]$Name,
    [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
    [int]$Age
  )

  # Note that I've wrapped this in a process block
  process {
    Write-Host "Hello, $Name. I see you are $Age years old."
  }
}

这是因为如上所述,如果未指定,函数默认为 end 块。但是,无论输入的管道对象如何,都会执行 endbegin 块。 process 只有在有数据传入时才会执行。使用管道变量将代码粘贴在 process 块内可以有效地阻止使用丢失数据的代码执行,这也是设计使然。

感谢@SantiagoSquarzon in the comments帮助我解决实际问题。

【讨论】:

    猜你喜欢
    • 2012-07-12
    • 2010-12-07
    • 2019-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多