[英]Can't get ValueFromPipeline to work in Powershell Module
我正在創建一個 Powershell 模塊,它充當與 RESTful API 接口的包裝器。
為了簡化腳本編寫者的工作,我提供了這個 Cmdlet 來“建立連接”到 API(本質上是創建 Worker 類對象並指定身份驗證令牌):
public class GetAPIConnection : Cmdlet
{
[Parameter(Position=0, Mandatory = true, ValueFromPipeline = true)]
[Alias("T","Password","Pwd","APIKey")]
public string Key { get; set; }
//This is the object I intend to send to the pipeline
private APIWorker API;
protected override void BeginProcessing()
{
base.BeginProcessing();
BuildOutputObject();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
WriteObject(API);
}
private BuildOutputObject()
{
API = new APIWorker(APIKey);
}
我遇到的問題是我希望其他 cmdlet 能夠從管道中接受Get-APIConnection生成的對象。 這是我如何嘗試在另一個 Cmdlet 中實現它的示例:
[Cmdlet(VerbsCommon.Get, @"SecurityGroups")]
[OutputType(typeof(string))]
public class GetSecurityGroups : Cmdlet
{
private APIWorker connection;
[Parameter(Position = 0,
Mandatory = true,
ValueFromPipeline = true)]
public APIWorker Connection
{
get { return connection; }
set
{
//Confirmation that set is actually being called
Console.WriteLine(@"Got here!");
connection = value;
}
}
[Parameter(Mandatory = true)]
public string Identity { get; set; }
private IEnumerable<string> Result;
protected override void BeginProcessing()
{
base.BeginProcessing();
BuildOutputObject();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
WriteObject(Result);
}
private BuildOutputObject()
{
Result = Connection.GetUserGroups(Identity);
}
因此,在導入模塊后,我可以使用 Get-APIConnection cmdlet 很好地創建 PoshAPI 對象:
>$API = Get-APIConnection -Key '[My Token]'
...如果我將 $Connection 作為命名參數傳遞,則Get-SecurityGroups工作:
>Get-SecurityGroups -Connection $API -Identity 'abcdefg'
Got here!
PowershellUsers
DotNetCOE
CompanyCarOwners
...
請注意“到了這里!” 發送到屏幕,表明確實在“Connection”參數上調用了set
...但是,即使我在“Get-SecurityGroups”中為“Connection”參數指定了ValueFromPipeline屬性,由於某種原因,我無法從管道傳遞 $API:
$API | Get-SecurityGroups -Identity 'abcdefg'
Get-SecurityGroups : Object reference not set to an instance of an object.
At line:1 char:7
+ $API | Get-SecurityGroups -Identity 'abcdefg'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-SecurityGroups], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,APIHelper.GetSecurityGroups
異常詳細信息表明在 BuildOutputObject() 中拋出了空引用異常:
> $error[0] | select *
PSMessageDetails :
Exception : System.NullReferenceException: Object reference not set to an instance of an object.
at APIHelper.GetSecurityGroups.BuildOutputObject()
at System.Management.Automation.Cmdlet.DoBeginProcessing()
at System.Management.Automation.CommandProcessorBase.DoBegin()
TargetObject :
CategoryInfo : NotSpecified: (:) [Get-SecurityGroups], NullReferenceException
FullyQualifiedErrorId : System.NullReferenceException,APIHelper.GetSecurityGroups
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
對我來說很有趣的是,鑒於 Connection 是一個強制性參數,並且沒有調用 set(因此沒有“到了這里!”),我完全能夠訪問 BuildOutputObject()。 提示我定義該參數不是正確的行為嗎??
任何幫助將不勝感激!
在 PowerShell 管道中,參數綁定在進程塊中完成,而不是在開始塊中。 如果您正在構建 PowerShell commandlet,我認為同樣的規則將適用。
看看這個例子,它類似於你的GetSecurityGroups
function Test-Pipeline {
[CmdletBinding()]
param (
[Parameter(Mandatory,ValueFromPipeline)]
[string]$a
)
BEGIN {Write-Verbose "begin $a"}
PROCESS {Write-Verbose "process $a"}
END {Write-Verbose 'end'}
}
在沒有管道的情況下調用它,會按您的預期提供輸出:
PS C:\> Test-Pipeline 'abc' -Verbose
VERBOSE: begin abc
VERBOSE: process abc
VERBOSE: end
但是,使用管道參數調用它的區別很小。 你看到了嗎?
PS C:\> 'abc' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: end
是的,在 begin 塊中 $a 沒有價值! 在這種情況下,$a 是簡單類型,因此沒有錯誤。 在您的情況下,您調用null
對象的方法,因此您收到錯誤。
原因很簡單。 在本例中可見。
PS C:\> 'abc','def' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: process def
VERBOSE: end
管道執行甚至在管道中每個 PowerShell 函數中的執行開始塊處理第一個值之前就開始了。
PS我想如果你嘗試這個,你也會得到意想不到的結果:
'[My Token]' | Get-APIConnection
簡單來說,我的問題是我需要將 BuildOutputObject() 方法移動到 ProcessRecord() 中。 當 BeginProcessing() 正在執行時,管道還沒有被讀取。 因此,在 ProcessRecord() 方法中設置輸出對象的值
[Cmdlet(VerbsCommon.Get, @"SecurityGroups")]
[OutputType(typeof(string))]
public class GetSecurityGroups : Cmdlet
{
private APIWorker connection;
[Parameter(Position = 0,
Mandatory = true,
ValueFromPipeline = true)]
public APIWorker Connection
{
get { return connection; }
set
{
//Confirmation that set is actually being called
Console.WriteLine(@"Got here!");
connection = value;
}
}
[Parameter(Mandatory = true)]
public string Identity { get; set; }
private IEnumerable<string> Result;
protected override void BeginProcessing()
{
base.BeginProcessing();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
BuildOutputObject();
WriteObject(Result);
}
private BuildOutputObject()
{
Result = Connection.GetUserGroups(Identity);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.