【发布时间】:2022-01-20 01:52:21
【问题描述】:
(PS 5.1.18362.145)
Set-Content -Value的参数定义为
Position : 1
ParameterSetName : __AllParameterSets
Mandatory : True
ValueFromPipeline : True
ValueFromPipelineByPropertyName : True
ValueFromRemainingArguments : False
HelpMessage :
HelpMessageBaseName :
HelpMessageResourceId :
DontShow : False
TypeId : System.Management.Automation.ParameterAttribute
TypeId : System.Management.Automation.AllowNullAttribute
TypeId : System.Management.Automation.AllowEmptyCollectionAttribute
重要的是,ValueFromPipeline 和 ValueFromPipelineByPropertyName 都是正确的。但是,测试表明ValueFromPipelineByPropertyName是无效的。
# Example 1
PS > [PSCustomObject]@{Value="frad"},
>> [PSCustomObject]@{Value="fred"},
>> [PSCustomObject]@{Value="frid"} | Set-Content testfile.txt
PS > Get-Content testfile.txt
@{value=frad}
@{value=fred}
@{value=frid}
但是,-Path 被指定为
ValueFromPipeline : False
ValueFromPipelineByPropertyName : True
测试ValueFromPipelineByPropertyname 产生以下结果
# Example 2
PS > [PSCustomObject]@{Path="frad.txt"},
>> [PSCustomObject]@{Path="fred.txt"},
>> [PSCustomObject]@{Path="frid.txt"} | Set-Content -Value teststring
PS > Get-Item *
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 20/01/2022 6:15 AM 12 frad.txt
-a---- 20/01/2022 6:15 AM 12 fred.txt
-a---- 20/01/2022 6:15 AM 12 frid.txt
-a---- 20/01/2022 6:09 AM 45 testfile.txt
PS > Get-Content f*
teststring
teststring
teststring
如果我们两者都做
# Example 3
PS > [PSCustomObject]@{Path="frad.txt";Value="frad"},
>> [PSCustomObject]@{Path="fred.txt";Value="fred"},
>> [PSCustomObject]@{Path="frid.txt";Value="frid"} | Set-Content
PS > Get-Item *
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 20/01/2022 6:21 AM 30 frad.txt
-a---- 20/01/2022 6:21 AM 30 fred.txt
-a---- 20/01/2022 6:21 AM 30 frid.txt
-a---- 20/01/2022 6:09 AM 45 testfile.txt
PS > Get-Content f*
@{Path=frad.txt; Value=frad}
@{Path=fred.txt; Value=fred}
@{Path=frid.txt; Value=frid}
很明显,Path 属性有效,但 Value 属性无效。使用 Bruce Payette 的 Windows Powershell in Action 摘录的解释(来自 SO 25550299)提供了一些启示。
...
通过完全匹配的值从管道绑定- 如果命令不是管道中的第一个命令,并且仍有未绑定的参数接受管道输入,请尝试绑定到完全匹配类型的参数。
如果未绑定,则通过转换从管道中按值绑定。 - 如果上一步失败,尝试使用类型转换进行绑定。
如果未绑定,则从管道中按名称绑定并精确匹配 - 如果上一步失败,则在输入对象上查找与参数名称匹配的属性。如果类型完全匹配,则绑定参数。
如果未绑定,则从管道中按名称绑定并进行转换。如果输入对象有一个名称与参数名称匹配的属性,并且该属性的类型可以转换为参数的类型,则绑定该参数。
这个问题可能是-Value 参数的类型是Object[],因此任何对象都已经完全匹配(步骤3),因此不会检查Value 属性的存在。这使得将 ValueFromPipeline 和 ValueFromPipelineByPropertyName 都设置为 true 以用于 Object(或 Object[])参数是毫无意义的。
这种分析似乎得到以下支持
PS > Trace-Command -Name ParameterBinding -PSHost -Expression {
>> [PSCustomObject]@{Path="blotto.txt";Value="blotto"} | Set-Content
>> }
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Set-Content]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Set-Content]
DEBUG: ParameterBinding Information: 0 : BIND cmd line args to DYNAMIC parameters.
DEBUG: ParameterBinding Information: 0 : DYNAMIC parameter object:
[Microsoft.PowerShell.Commands.FileSystemContentWriterDynamicParameters]
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Set-Content]
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Set-Content]
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE = [System.Management.Automation.PSCustomObject]
DEBUG: ParameterBinding Information: 0 : RESTORING pipeline parameter's original values
DEBUG: ParameterBinding Information: 0 : Parameter [Value] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [@{path=blotto.txt; value=blotto}] to parameter [Value]
DEBUG: ParameterBinding Information: 0 : Binding collection parameter Value: argument type [PSObject],
parameter type [System.Object[]], collection type Array, element type [System.Object], no coerceElementType
DEBUG: ParameterBinding Information: 0 : Creating array with element type [System.Object] and 1 elements
DEBUG: ParameterBinding Information: 0 : Argument type PSObject is not IList, treating this as scalar
DEBUG: ParameterBinding Information: 0 : Adding scalar element of type PSObject to array position 0
DEBUG: ParameterBinding Information: 0 : BIND arg [System.Object[]] to param [Value] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO
COERCION
DEBUG: ParameterBinding Information: 0 : Parameter [Path] PIPELINE INPUT ValueFromPipelineByPropertyName NO
COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [blotto.txt] to parameter [Path]
DEBUG: ParameterBinding Information: 0 : Binding collection parameter Path: argument type [String], parameter
type [System.String[]], collection type Array, element type [System.String], no coerceElementType
DEBUG: ParameterBinding Information: 0 : Creating array with element type [System.String] and 1 elements
DEBUG: ParameterBinding Information: 0 : Argument type String is not IList, treating this as scalar
DEBUG: ParameterBinding Information: 0 : Adding scalar element of type String to array position 0
DEBUG: ParameterBinding Information: 0 : BIND arg [System.String[]] to param [Path] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName WITH
COERCION
DEBUG: ParameterBinding Information: 0 : Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName WITH
COERCION
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Set-Content]
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing
PS > type blotto.txt
@{path=blotto.txt; value=blotto}
可以看出,-Value 参数已成功绑定到输入对象 ValueFromPipeline(即步骤 3),没有强制。
那么问题是,
- 自 PS 5.1 以来此问题是否已修复(假设其他人同意这是一个错误)?
- 有没有一种方法可以将管道对象的 Value 属性用于
Set-Content(或相应的 cmdlet 参数可以接收[Object]的任何属性)而不会失去对对象其余部分的访问权(在之前的版本中任何修复)? (例如$object.Value | Set-Content ...不起作用,请参见上面的示例 3)
我自己无法检查问题 1(系统限制),否则我可能会在 GitHub 上将此问题作为 PowerShell 问题提出。而且,是的,我知道示例 3 是一个有点做作的、深奥的边缘情况,但示例 1 更有可能(并且尝试将 3 作为测试设置的一部分是我发现这一点的原因)。
【问题讨论】:
标签: powershell