当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 创建修改系统的 Cmdlet

作者:精品下载站 日期:2024-12-14 02:49:16 浏览:15 分类:玩电脑

创建修改系统的 Cmdlet


有时,cmdlet 必须修改系统的运行状态,而不仅仅是 Windows PowerShell 运行时的状态。在这些情况下,cmdlet 应允许用户有机会确认是否进行更改。

为了支持确认,cmdlet 必须做两件事。

  • 通过将 SupportsShouldProcess 关键字设置为 true 来指定 System.Management.Automation.CmdletAttribute 属性时,声明 cmdlet 支持确认。

  • 在执行 cmdlet 期间调用 System.Management.Automation.Cmdlet.ShouldProcess(如以下示例所示)。

通过支持确认,cmdlet 公开 Windows PowerShell 提供的 ConfirmWhatIf 参数,并且还满足 cmdlet 的开发指南(有关 cmdlet 开发指南的更多信息,请参阅 Cmdlet 开发指南。)。

改变系统

“更改系统”行为是指可能在 Windows PowerShell 之外更改系统状态的任何 cmdlet。例如,停止进程、启用或禁用用户帐户或向数据库表添加行都是应该确认的系统更改。相反,读取数据或建立瞬时连接的操作不会改变系统,通常不需要确认。对于其效果仅限于 Windows PowerShell 运行时内部的操作(例如 set-variable),也不需要确认。可能会或可能不会进行持久更改的 Cmdlet 应声明 SupportsShouldProcess 并仅在它们要进行持久更改时才调用 System.Management.Automation.Cmdlet.ShouldProcess。

笔记

ShouldProcess 确认仅适用于 cmdlet。如果命令或脚本通过直接调用 .NET 方法或属性,或者通过调用 Windows PowerShell 外部的应用程序来修改系统的运行状态,则这种形式的确认将不可用。

StopProc Cmdlet

本主题介绍 Stop-Proc cmdlet,该 cmdlet 尝试停止使用 Get-Proc cmdlet 检索的进程(在创建第一个 Cmdlet 中进行了描述)。

定义 Cmdlet

创建 cmdlet 的第一步始终是命名 cmdlet 并声明实现该 cmdlet 的 .NET 类。因为您正在编写一个 cmdlet 来更改系统,所以应该对其进行相应的命名。此 cmdlet 停止系统进程,因此此处选择的动词名称是“Stop”,由 System.Management.Automation.Verbslifecycle 类定义,并使用名词“Proc”指示该 cmdlet 停止进程。有关批准的 cmdlet 动词的详细信息,请参阅 Cmdlet 动词名称。

以下是此 Stop-Proc cmdlet 的类定义。

[Cmdlet(VerbsLifecycle.Stop, "Proc",
        SupportsShouldProcess = true)]
public class StopProcCommand : Cmdlet

请注意,在 System.Management.Automation.CmdletAttribute 声明中,SupportsShouldProcess 属性关键字设置为 true 以使 cmdlet 能够调用 System.Management.Automation。 Cmdlet.ShouldProcess 和 System.Management.Automation.Cmdlet.ShouldContinue。如果不设置此关键字,用户将无法使用 ConfirmWhatIf 参数。

极具破坏性的行为

有些操作具有极大的破坏性,例如重新格式化活动硬盘分区。在这些情况下,cmdlet 在声明 System.Management.Automation.CmdletAttribute 属性时应设置 ConfirmImpact=ConfirmImpact.High。即使用户未指定 Confirm 参数,此设置也会强制 cmdlet 请求用户确认。但是,cmdlet 开发人员应避免过度使用 ConfirmImpact 来执行可能具有破坏性的操作,例如删除用户帐户。请记住,如果 ConfirmImpact 设置为 System.Management.Automation.ConfirmImpact

同样,某些操作不太可能具有破坏性,尽管理论上它们确实会修改 Windows PowerShell 之外的系统的运行状态。此类 cmdlet 可以将 ConfirmImpact 设置为 System.Management.Automation.Confirmimpact.Low。这将绕过用户要求仅确认中等影响和高影响操作的确认请求。

定义系统修改参数

本节介绍如何定义 cmdlet 参数,包括支持系统修改所需的参数。如果您需要有关定义参数的一般信息,请参阅添加处理命令行输入的参数。

Stop-Proc cmdlet 定义三个参数:NameForcePassThru

Name 参数对应于流程输入对象的Name 属性。请注意,此示例中的 Name 参数是必需的,因为如果没有要停止的命名进程,cmdlet 将失败。

Force 参数允许用户覆盖对 System.Management.Automation.Cmdlet.ShouldContinue 的调用。事实上,任何调用 System.Management.Automation.Cmdlet.ShouldContinue 的 cmdlet 都应具有 Force 参数,以便在指定 Force 时,cmdlet 会跳过对 System.out.println() 的调用。 Management.Automation.Cmdlet.ShouldContinue 并继续操作。请注意,这不会影响对 System.Management.Automation.Cmdlet.ShouldProcess 的调用。

PassThru 参数允许用户指示 cmdlet 是否通过管道传递输出对象(在本例中是在进程停止后)。请注意,此参数与 cmdlet 本身相关,而不是与输入对象的属性相关。

以下是 Stop-Proc cmdlet 的参数声明。

[Parameter(
           Position = 0,
           Mandatory = true,
           ValueFromPipeline = true,
           ValueFromPipelineByPropertyName = true
)]
public string[] Name
{
  get { return processNames; }
  set { processNames = value; }
}
private string[] processNames;

/// <summary>
/// Specify the Force parameter that allows the user to override
/// the ShouldContinue call to force the stop operation. This
/// parameter should always be used with caution.
/// </summary>
[Parameter]
public SwitchParameter Force
{
  get { return force; }
  set { force = value; }
}
private bool force;

/// <summary>
/// Specify the PassThru parameter that allows the user to specify
/// that the cmdlet should pass the process object down the pipeline
/// after the process has been stopped.
/// </summary>
[Parameter]
public SwitchParameter PassThru
{
  get { return passThru; }
  set { passThru = value; }
}
private bool passThru;

重写输入处理方法

cmdlet 必须重写输入处理方法。以下代码说明了示例 Stop-Proc cmdlet 中使用的 System.Management.Automation.Cmdlet.ProcessRecord 重写。对于每个请求的进程名称,此方法确保该进程不是特殊进程,尝试停止该进程,然后在指定了 PassThru 参数的情况下发送一个输出对象。

protected override void ProcessRecord()
{
  foreach (string name in processNames)
  {
    // For every process name passed to the cmdlet, get the associated
    // process(es). For failures, write a non-terminating error
    Process[] processes;

    try
    {
      processes = Process.GetProcessesByName(name);
    }
    catch (InvalidOperationException ioe)
    {
      WriteError(new ErrorRecord(ioe,"Unable to access the target process by name",
                 ErrorCategory.InvalidOperation, name));
      continue;
    }

    // Try to stop the process(es) that have been retrieved for a name
    foreach (Process process in processes)
    {
      string processName;

      try
      {
        processName = process.ProcessName;
      }

      catch (Win32Exception e)
        {
          WriteError(new ErrorRecord(e, "ProcessNameNotFound",
                     ErrorCategory.ReadError, process));
          continue;
        }

        // Call Should Process to confirm the operation first.
        // This is always false if WhatIf is set.
        if (!ShouldProcess(string.Format("{0} ({1})", processName,
                           process.Id)))
        {
          continue;
        }
        // Call ShouldContinue to make sure the user really does want
        // to stop a critical process that could possibly stop the computer.
        bool criticalProcess =
             criticalProcessNames.Contains(processName.ToLower());

        if (criticalProcess &&!force)
        {
          string message = String.Format
                ("The process \"{0}\" is a critical process and should not be stopped. Are you sure you wish to stop the process?",
                processName);

          // It is possible that ProcessRecord is called multiple times
          // when the Name parameter receives objects as input from the
          // pipeline. So to retain YesToAll and NoToAll input that the
          // user may enter across multiple calls to ProcessRecord, this
          // information is stored as private members of the cmdlet.
          if (!ShouldContinue(message, "Warning!",
                              ref yesToAll,
                              ref noToAll))
          {
            continue;
          }
        } // if (criticalProcess...
        // Stop the named process.
        try
        {
          process.Kill();
        }
        catch (Exception e)
        {
          if ((e is Win32Exception) || (e is SystemException) ||
              (e is InvalidOperationException))
          {
            // This process could not be stopped so write
            // a non-terminating error.
            string message = String.Format("{0} {1} {2}",
                             "Could not stop process \"", processName,
                             "\".");
            WriteError(new ErrorRecord(e, message,
                       ErrorCategory.CloseError, process));
                       continue;
          } // if ((e is...
          else throw;
        } // catch

        // If the PassThru parameter argument is
        // True, pass the terminated process on.
        if (passThru)
        {
          WriteObject(process);
        }
    } // foreach (Process...
  } // foreach (string...
} // ProcessRecord

调用ShouldProcess方法

在对系统运行状态进行更改(例如删除文件)之前,cmdlet 的输入处理方法应调用 System.Management.Automation.Cmdlet.ShouldProcess 方法来确认操作的执行。这允许 Windows PowerShell 运行时在 shell 中提供正确的“WhatIf”和“Confirm”行为。

笔记

如果 cmdlet 声明它支持 should process,但无法进行 System.Management.Automation.Cmdlet.ShouldProcess 调用,则用户可能会意外修改系统。

对 System.Management.Automation.Cmdlet.ShouldProcess 的调用会将要更改的资源名称发送给用户,Windows PowerShell 运行时会考虑任何命令行设置或首选项变量来确定应向用户显示的内容。

以下示例显示了从示例 Stop-Proc cmdlet 中的 System.Management.Automation.Cmdlet.ProcessRecord 方法的重写中对 System.Management.Automation.Cmdlet.ShouldProcess 的调用。

if (!ShouldProcess(string.Format("{0} ({1})", processName,
                   process.Id)))
{
  continue;
}

调用ShouldContinue方法

对 System.Management.Automation.Cmdlet.ShouldContinue 方法的调用会向用户发送辅助消息。此调用是在调用 System.Management.Automation.Cmdlet.ShouldProcess 返回 trueForce 参数未设置为 true 后进行的。然后用户可以提供反馈以表明是否应该继续操作。您的 cmdlet 调用 System.Management.Automation.Cmdlet.ShouldContinue 作为对潜在危险的系统修改的额外检查,或者当您想要向用户提供“全部是”和“全部否”选项时。

以下示例显示了从示例 Stop-Proc cmdlet 中的 System.Management.Automation.Cmdlet.ProcessRecord 方法的重写中对 System.Management.Automation.Cmdlet.ShouldContinue 的调用。

if (criticalProcess &&!force)
{
  string message = String.Format
        ("The process \"{0}\" is a critical process and should not be stopped. Are you sure you wish to stop the process?",
        processName);

  // It is possible that ProcessRecord is called multiple times
  // when the Name parameter receives objects as input from the
  // pipeline. So to retain YesToAll and NoToAll input that the
  // user may enter across multiple calls to ProcessRecord, this
  // information is stored as private members of the cmdlet.
  if (!ShouldContinue(message, "Warning!",
                      ref yesToAll,
                      ref noToAll))
  {
    continue;
  }
} // if (criticalProcess...

停止输入处理

进行系统修改的 cmdlet 的输入处理方法必须提供停止输入处理的方法。对于此 Stop-Proc cmdlet,将从 System.Management.Automation.Cmdlet.ProcessRecord 方法调用 System.Diagnostics.Process.Kill* 方法。由于 PassThru 参数设置为 true,System.Management.Automation.Cmdlet.ProcessRecord 还会调用 System.Management.Automation.Cmdlet.WriteObject 将流程对象发送到管道。

代码示例

有关完整的 C# 示例代码,请参阅 StopProcessSample01 示例。

定义对象类型和格式

Windows PowerShell 使用 .Net 对象在 cmdlet 之间传递信息。因此,小命令可能需要定义其自己的类型,或者小命令可能需要扩展另一个小命令提供的现有类型。有关定义新类型或扩展现有类型的更多信息,请参阅扩展对象类型和格式。

构建 Cmdlet

实施 cmdlet 后,必须通过 Windows PowerShell 管理单元向 Windows PowerShell 注册。有关注册 cmdlet 的详细信息,请参阅如何注册 Cmdlet、提供程序和主机应用程序。

测试 Cmdlet

当您的 cmdlet 已注册到 Windows PowerShell 后,您可以通过在命令行上运行它来测试它。以下是测试 Stop-Proc cmdlet 的几个测试。有关从命令行使用 cmdlet 的详细信息,请参阅 Windows PowerShell 入门。

  • 启动 Windows PowerShell 并使用 Stop-Proc cmdlet 停止处理,如下所示。由于 cmdlet 将 Name 参数指定为强制参数,因此 cmdlet 会查询该参数。

    PS> stop-proc
    

    将出现以下输出。

    Cmdlet stop-proc at command pipeline position 1
    Supply values for the following parameters:
    Name[0]:
    
  • 现在让我们使用 cmdlet 来停止名为“NOTEPAD”的进程。该 cmdlet 要求您确认该操作。

    PS> stop-proc -Name notepad
    

    将出现以下输出。

    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "notepad (4996)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): Y
    
  • 使用 Stop-Proc 如图所示停止名为“WINLOGON”的关键进程。系统会提示您执行此操作并发出警告,因为这将导致操作系统重新启动。

    PS> stop-proc -Name Winlogon
    

    将出现以下输出。

    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "winlogon (656)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): Y
    Warning!
    The process " winlogon " is a critical process and should not be stopped. Are you sure you wish to stop the process?
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): N
    
  • 现在让我们尝试停止 WINLOGON 进程而不收到警告。请注意,此命令条目使用 Force 参数来覆盖警告。

    PS> stop-proc -Name winlogon -Force
    

    将出现以下输出。

    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "winlogon (656)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): N
    

参见

添加处理命令行输入的参数

扩展对象类型和格式

如何注册 Cmdlet、提供程序和主机应用程序

Windows PowerShell SDK

Cmdlet 示例

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯