在调用Powershell Cmdlet之间保持状态的最佳实践是什么?

时间:2013-12-11 08:12:47

标签: c# powershell cmdlets

我是Powershell开发的新手,我正在尝试用C#编写一个Powershell Cmdlet,它将用作REST API的接口。

我希望有一些设置Cmdlet,用户将被提示输入REST API的Uri,用户名和密码,然后调用Cmldlets,如Get-Item,而不必输入这些参数。非常类似于Powershell的Azure提供程序,您可以通过调用Select-AzureSubscription设置当前订阅,然后再调用Save-AzureVhd而无需再次输入订阅名称。

在不同Cmdlet的调用之间保持状态的最佳做法是什么?

编辑: 我不确定这是解决它的最佳方法,但我所做的是我添加了一个保持状态的单例类。 我有一个Cmdlet,Select-Project -Name MyProject,它在我的单例类中设置一个公共属性,然后我的其他Cmdlet可以访问该属性。

3 个答案:

答案 0 :(得分:1)

如果他们正在运行V3或更高版本,您可以让设置在$ PSDefaultParameterValues中设置这些值。

见:

get-help about_parameters_default_values

有关设置值的详细信息。

答案 1 :(得分:0)

也许像PowerShell的CimSession支持?您使用new-cimsession创建会话(包含状态),然后将cimsession对象传递给其他各种cmdlet。这不会像OP中提到的那样使用get-item。

然而,OP中的EDIT描述了一个不太可能与get-item一起使用的实现(如果我理解正确的话)。

如果get-item的支持确实是一个要求,那么我相信PS提供商(如get-PSProvider)将是可行的方法。 PS提供商将使用像get-item这样的cmdlet,并且可以通过PSDrives保存状态(如在get-PSDrive中)。

答案 2 :(得分:0)

您正在寻找PSCmdlet.SessionState

我通过创建一个小的桥类MyCmdlet解决了相同的问题,我自己的所有cmdlet都来自该桥类,它包含管理会话状态的助手以及定义包含您所要东西的对象的定义想要坚持。在这种情况下,我将组成一些简单的内容,例如用户名和数据库名称。

// In MyCmdlet.cs

public class MyStateInfo {
  public string Username { get; set;}
  public string DbName { get; set;}
}

protected abstract class MyCmdlet : PSCmdlet {
    private const string StateName = "_Blah";

    protected MyStateInfo getState()
    {
        if ( ! SessionState.PSVariable.GetValue(StateName, null) is MyStateInfo s))
            SessionState.PSVariable.Set(StateName, s = new MyStateInfo());

        return s;
    }
}

这时,您所有的cmdlet都应继承自MyCmdletgetState()将始终返回现有变量或新变量:对该类的更改将在同一会话中保留。

可能有很多方法可以将其集成到您的整个cmdlet参数设计中,这对我来说还是一个新手,所以我不确定这是否是最佳实践,但是我已经通过创建一个辅助cmdlet解决了它设置初始值:

[Cmdlet(VerbsCommon.Set, "MyUsername")]
public class Set_MyUsername : MyCmdlet {
  [Parameter(Mandatory = true, Position = 1)] 
  public string Username {get; set; }

  protected override void ProcessRecord()
  {
       base.ProcessRecord();

       WriteVerbose($"Saving {Username} in session state");
       getState().Username = Username;
  }
}

然后,稍后的cmdlet需要对此进行处理:

[Cmdlet(VerbsCommunication.Connect, "Database")]
public class Connect_Database : MyCmdlet {
    [Parameter(Mandatory = false)]
    public string Username { get; set; }

    // other parameters here

    protected override void BeginProcessing()
    {
        base.BeginProcessing();

        if (Username == null)
            Username = getState().Username;

        if (Username == null)
        { // ERROR HERE: missing username }
    }
    // more stuff
}

然后,您的Connect-Database将使用一个显式的-Username steve参数(不协商或触摸会话状态),但是如果没有该参数,它将把它从会话状态对象中拉出。如果此后用户名仍然丢失,则失败(可能是ThrowTerminatingError)。

您的Select-Project可以以相同的方式工作。

我通常提供一个Get-MyState cmdlet,该cmdlet仅将状态对象写入管道输出,主要用于调试。如果您的应用程序保证将这些内容分开,则不仅限于一个变量。