(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 )提供了一些啟示。
...
Bind from the pipeline by value with exact match- If the command is not the first command in the pipeline and there are still unbound parameters that take pipeline input, try to bind to a parameter that matches the type exactly.
If not bound, then bind from the pipe by value with conversion. - If the previous step failed, try to bind using a type conversion.
If not bound, then bind from the pipeline by name with exact match - If the previous step failed, look for a property on the input object that matches the name of the parameter. If the types exactly match, bind the parameter.
If not bound, then bind from the pipeline by name with conversion. If the input object has a property whose name matches the name of a parameter, and the type of the property is convertible to the type of the parameter, bind the parameter.
The problem with this could be that the type of the -Value parameter is Object[], thus any object will already be an exact match (step 3) and so existence of the Value property will not be checked. This then makes it purposeless to have both ValueFromPipeline and ValueFromPipelineByPropertyName set to true for an Object (or Object[]) parameter.
This analysis would seem to be supported by the following
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}
As can be seen, the -Value parameter is successfully bound, without coercion, to the input object as ValueFromPipeline (i.e. step 3).
The questions then are,
- has this been fixed since PS 5.1 (assuming that others agree that it is a bug)?
- 有沒有辦法利用管道物件的 Value 屬性
Set-Content(或相應的 cmdlet 引數可以接收的任何屬性[Object])而不會失去對物件其余部分的訪問(在任何修復之前的版本中)?(例如$object.Value | Set-Content ...不起作用,請參見上面的示例 3)
我自己無法檢查問題 1(系統限制),否則我可能會在 GitHub 上將此問題作為 PowerShell 問題提出。而且,是的,我知道示例 3 是一個有點做作的、深奧的邊緣情況,但示例 1 更有可能(并且嘗試將 3 作為測驗設定的一部分是我發現這一點的原因)。
uj5u.com熱心網友回復:
回復 1:
不,這是一個Set-Content尚未在 PowerShell (Core) v6 中修復的設計缺陷,截至撰寫本文時的最新版本 v7.2.1
修復將需要從to重新鍵入-Value引數(請參閱底部的概念證明)。[object[]][string[]]
這樣,具有屬性的非字串輸入物件將被.Value該屬性(根據您的問題中參考的系結規則,首先(因為 .NET 中的所有物件都派生自)。ValueFromPipelineByPropertyValue[object[]]ValueFromPipeline[object]
換句話說:構成設計缺陷的原因是,將[object]or型別的[object[]引數宣告為兩者是沒有意義 ValueFromPipeline 的 ValueFromPipelineByPropertyValue,因為只有ValueFromPipeline行為才會生效。
回復 2:
不,很遺憾沒有。
它還需要上面建議的修復,因為在這種情況下,由于引數的型別,即使使用延遲系結腳本塊引數也無濟于事。-Value[object[]]
# !! Does NOT work as of v7.2.1, because delay-bind script blocks
# !! only work with parameters typed *other* than [object] or [scriptblock]
# !! Currently, *verbatim* ' $_.Value ' is used, i.e.
# !! the immediate *stringification* of the script block.
[PSCustomObject]@{Path="frad.txt";Value="frad"} |
Set-Content -Value { $_.Value }
如果沒有建議的修復,您將不得不求助于 - 低效 - 解決方法:將物件通過管道傳輸到ForEach-Object并呼叫Set-Content那里,顯式使用每個輸入物件的屬性。
這是修復的簡化概念證明:
function Set-Content {
[CmdletBinding()]
param(
[Parameter(ValueFromPipelineByPropertyName)]
[string[]] $Path,
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string[]] $Value # Note the type of [string[]] rather than [object[]]
)
process {
foreach ($p in $Path) {
# Delegate to the original Set-Content, with explicit arguments.
Microsoft.PowerShell.Management\Set-Content -Path $p -Value $Value
}
}
}
有了上面的Set-Content覆寫:
[PSCustomObject]@{Path="frad.txt";Value="frad"},
[PSCustomObject]@{Path="fred.txt";Value="fred"} | Set-Content
按預期創建frad.txt帶有內容的檔案和帶有內容frad的檔案。fred.txtfred
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/418473.html
標籤:
