无法让 ValueFromPipeline 在 Powershell 模块中工作
Can't get ValueFromPipeline to work in Powershell Module
我正在创建一个 Powershell 模块,作为与 RESTful API 接口的包装器。
为了简化脚本编写者的工作,我提供此 Cmdlet 以“建立连接”到 API(实质上是创建 Worker class 对象并指定身份验证令牌):
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
...
注意“到了这里!”被发送到屏幕,表明 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 : {}
有趣的是,我能够访问 BuildOutputObject(),因为 Connection 是一个强制参数,并且没有调用它(因此没有“Got here!”)。提示我定义该参数不是正确的行为吗??
如有任何帮助,我们将不胜感激!
在 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
是的,在开始块 $a 没有值!在本例中,$a 是简单类型,因此没有错误。在你的例子中,你调用了 null
对象的方法,所以你得到了错误。
原因很简单。在这个例子中是可见的。
PS C:\> 'abc','def' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: process def
VERBOSE: end
管道执行甚至在管道中每个 PowerShell 函数中的执行开始块处理第一个值之前就开始了。
P.S。如果您尝试这样做,我想您也会得到意想不到的结果:
'[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);
}
我正在创建一个 Powershell 模块,作为与 RESTful API 接口的包装器。
为了简化脚本编写者的工作,我提供此 Cmdlet 以“建立连接”到 API(实质上是创建 Worker class 对象并指定身份验证令牌):
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
...
注意“到了这里!”被发送到屏幕,表明 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 : {}
有趣的是,我能够访问 BuildOutputObject(),因为 Connection 是一个强制参数,并且没有调用它(因此没有“Got here!”)。提示我定义该参数不是正确的行为吗??
如有任何帮助,我们将不胜感激!
在 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
是的,在开始块 $a 没有值!在本例中,$a 是简单类型,因此没有错误。在你的例子中,你调用了 null
对象的方法,所以你得到了错误。
原因很简单。在这个例子中是可见的。
PS C:\> 'abc','def' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: process def
VERBOSE: end
管道执行甚至在管道中每个 PowerShell 函数中的执行开始块处理第一个值之前就开始了。
P.S。如果您尝试这样做,我想您也会得到意想不到的结果:
'[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);
}