首页
/ PowerShell 二进制模块实战:用 .NET Standard 2.0 和 dotnet CLI 构建跨平台 Cmdlet

PowerShell 二进制模块实战:用 .NET Standard 2.0 和 dotnet CLI 构建跨平台 Cmdlet

2026-09-05 21:51:57作者:何将鹤

本文基于 PowerShell 仓库中的 docs/cmdlet-example/command-line-simple-example.md 指南,完整演示如何使用 .NET Core 命令行工具(dotnet CLI)从零创建一个基于 .NET Standard 2.0 类库的 PowerShell 二进制模块(Binary Module)。读完本文,你将掌握:如何用 dotnet new classlib 搭建工程、引用 PowerShell Standard Library 包、编写 C# Cmdlet 并构建、导入和调用;同时理解 .NET Standard 2.0 程序集为何能同时被 PowerShell Core 与 Windows PowerShell 加载,以及在没有 .NET Framework 4.7.1 的 Windows 系统上出现 netstandard.dll 缺失错误时的完整修复步骤。

为什么选择 .NET Standard 2.0

PowerShell 存在两个实现:PowerShell Core(.NET Core 之上,跨平台)和 Windows PowerShell(.NET Framework 之上,3.0 及以上版本)。如果二进制模块的目标框架是 .NET Standard 2.0 类库,那么同一份程序集可以同时导入这两种实现——你不必为它们分别构建和分发不同的程序集。

这一设计在仓库源码中有直接印证:Cmdlet 的基类 PSCmdlet 就定义在 System.Management.Automation 核心引擎源码 中,外部二进制模块通过引用该程序集的 API(或 PowerShell Standard Library 中对应的 API 子集)来派生 PSCmdlet/Cmdlet。仓库同时提供了 Visual Studio 图形界面方式构建同一 Cmdlet 的对照指南,与本文的 CLI 方式互为补充。

前提条件

  • PowerShell Core 和/或 Windows PowerShell

    本例可在 PowerShell Core 支持的任何操作系统上进行。仓库 README.md 中的 "Get PowerShell" 部分列出了各平台的支持情况与安装方式。

    说明:在 Windows 10 周年更新(Anniversary Update)及以上版本上,可以通过 WSL(Windows Subsystem for Linux)控制台构建模块。若要导入和使用该模块,需要为你的 Linux 发行版安装对应版本的 PowerShell Core。可以运行 lsb_release -a 查看当前 WSL 的发行版信息。

  • .NET Core 2.x SDK

    为你的操作系统下载并安装 .NET Core 2.x SDK。在 Linux 上建议使用包管理器安装,并按你的发行版(如 RHEL、Debian 等)选择对应的安装说明。

    注:本指南以 .NET Core 2.x SDK 为适用前提;当前仓库自身构建使用更新版本的 .NET SDK(见仓库根目录的 global.json,其中锁定了具体的 SDK 版本),但本示例面向的是编写第三方二进制模块的开发者,使用 2.x SDK 即可复现全部步骤。

创建 .NET Standard 2.0 二进制模块

第 1 步:确认 dotnet CLI 版本

dotnet --version

输出应为 2.0.0 或更高。如果返回主版本号为 1,说明尚未安装 .NET Core 2.x SDK,请安装后重启 Shell 再重新执行。

第 2 步:创建 classlib 启动项目

dotnet new classlib --name MyModule

classlib(类库)项目模板的默认目标框架就是 .NET Standard 2.0,正好满足跨 PowerShell 实现的要求。

第 3 步:添加 global.json 锁定 SDK 版本

cd MyModule
dotnet new globaljson --sdk-version 2.0.0

global.json 的作用是显式声明项目所需的 .NET Core SDK 版本(本例为 2.0.0)。这在机器上安装了多个 SDK 版本时尤其重要,可以防止 dotnet 工具自动挑选到不兼容的高版本。

这一做法与仓库自身的实践一致:PowerShell 主仓库根目录就有一份 global.json 文件来锁定 SDK 版本,从源码结构看这是 .NET 项目应对多 SDK 共存的标准手段。

第 4 步:添加 PowerShell Standard Library 包

dotnet add package PowerShellStandard.Library --version 3.0.0-preview-01

PowerShellStandard.Library 包提供 System.Management.Automation 程序集的精简 API 面(即 PowerShell Standard 3.0 定义的子集)。注意:随着该库发布新版本,应把命令中的版本号更新为最新版。

该包 API 面的约束在仓库的测试中也有验证痕迹,例如 StandardLibraryTypes.Tests.ps1 就测试了标准库类型在会话中的行为,可以从中了解 Standard Library 类型系统与引擎的交互。

第 5 步:编写 Cmdlet 源码

用编辑器打开 Class1.cs,将原有代码替换为以下代码:

using System;
using System.Management.Automation;

namespace MyModule
{
    [Cmdlet(VerbsCommunications.Write, "TimestampedMessage")]
    public class WriteTimestampedMessageCommand : PSCmdlet
    {
        [Parameter(Position=1)]
        public string Message { get; set; } = string.Empty;

        protected override void EndProcessing()
        {
            string timestamp = DateTime.Now.ToString("u");
            this.WriteObject($"[{timestamp}] - {this.Message}");
            base.EndProcessing();
        }
    }
}

源码要点解析:

  • [Cmdlet(VerbsCommunications.Write, "TimestampedMessage")]CmdletAttribute 将普通 C# 类注册为 Cmdlet。动词取自 PowerShell 批准动词集 VerbsCommunications.Write,与名词 TimestampedMessage 组合后,命令名为 Write-TimestampedMessage。命名必须遵循 "动词-名词" 规范,仓库引擎源码 CommandBase.cs 中可以看到 Cmdlet 元数据(CommandMetadata)就是围绕这一约定构建的。
  • public class WriteTimestampedMessageCommand : PSCmdlet:继承引擎提供的基类 PSCmdlet(定义于 MshCmdlet.cs),它封装了参数绑定、事务支持等基础设施;如果不需要这些能力,也可以像 Visual Studio 版示例 那样直接继承更轻量的 Cmdlet
  • [Parameter(Position=1)]:声明 Message 属性为位置参数,因此调用时可以直接按位置传值(如 Write-TimestampedMessage "Test message.")。
  • EndProcessing():Cmdlet 生命周期中处理完所有输入后的收尾阶段。本例没有逐条输入,因此在 EndProcessing 中一次性输出带时间戳的消息。仓库引擎内大量内置命令同样覆写该方法做收尾输出,例如 GetCommandCommand.csModules/NewModuleCommand.cs
  • DateTime.Now.ToString("u")"u" 为通用排序格式(UTC,形如 2016-02-01 12:34:56Z),保证时间戳格式稳定可排序。
  • this.WriteObject(...):向输出流写出对象,即 Cmdlet 的标准输出方式。

第 6 步:构建项目

dotnet build

构建产物位于 bin/Debug/netstandard2.0/MyModule.dll,其程序集目标为 netstandard2.0。

第 7 步:导入模块并调用命令

注意:前面的步骤在 Linux 上也可以换用 Bash 等其他 Shell 完成;到这一步请确保运行的是 PowerShell Core

cd 'bin/Debug/netstandard2.0'
Import-Module ./MyModule.dll
Write-TimestampedMessage "Test message."

导入后,Write-TimestampedMessage 命令即出现在当前会话中,执行后会输出一行带 UTC 时间戳的消息。

在 Windows PowerShell 中使用 .NET Standard 2.0 模块

一个作为 .NET Standard 2.0 类库编译的 .NET 程序集,理论上可以加载到 .NET Core 2.x 应用(如 PowerShell Core)和 .NET Framework 4.6.1 及以上应用(如 Windows PowerShell)中,从而实现"一份程序集,两个宿主"。

但有一个前提:宿主应用需要已经针对 .NET Standard 2.0 编译、或声明了对 .NET Standard 库的支持。这样构建系统才能提供必要的绑定重定向(binding redirects)以及 facade/shim 程序集,让 .NET Standard 2.0 库在运行中的应用上下文里找到所需的 .NET Framework 类型。

好消息:.NET Framework 4.7.1 与 Windows 10 秋季创造者更新(Fall Creators Update)已修复这一问题——这些系统上无需修改或重新编译,.NET Standard 2.0 二进制模块即可直接在 Windows PowerShell 中工作。

坏消息:在未升级到 .NET Framework 4.7.1 的 Windows 系统上(如 Windows 10 1703 或更低版本),模块会出错。

复现错误:netstandard.dll 缺失

  1. MyModule.dll 拷贝到一台 Windows 机器的某个文件夹。

  2. 导入模块:

    Import-Module .\MyModule.dll
    

    注意:此时导入本身不会报错。

  3. 执行命令:

    Write-TimestampedMessage "Test message."
    

    在没有 .NET Framework 4.7.1 的 Windows 10 CU(1703 或更低版本)上会得到如下错误:

    Write-TimestampedMessage : Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral,
    PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.
    At line:1 char:1
    + Write-TimestampedMessage "Test message."
    + ~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], FileNotFoundException
        + FullyQualifiedErrorId : System.IO.FileNotFoundException
    

如果你这边命令执行成功了,说明你的系统已更新到 .NET Framework 4.7.1。否则,这个错误意味着 MyModule.dll 找不到 Windows PowerShell 所用 .NET Framework 版本对应的 netstandard.dll 实现程序集。

修复方案:补齐 netstandard.dll 实现程序集

如果你已安装(或准备安装)Windows 版的 .NET Core SDK,可以在以下目录中找到面向 .NET 4.6.1 的 netstandard.dll 实现程序集:

C:\Program Files\dotnet\sdk\<version-number>\Microsoft\Microsoft.NET.Build.Extensions\net461\lib

其中 <version-number> 取决于你安装的 SDK 版本。把该目录下的 netstandard.dll 复制到 MyModule.dll 所在目录,Write-TimestampedMessage 即可正常工作。完整操作步骤:

  1. 安装 Windows 版 .NET Core SDK(如尚未安装)。

  2. 启动一个新的 Windows PowerShell 控制台。注意:二进制程序集一旦加载进 PowerShell 就无法卸载,重启 PowerShell 是重新加载 MyModule.dll 的必要条件。

  3. 将 .NET 4.6.1 的 netstandard.dll 实现程序集复制到模块所在目录:

    cd 'path-to-where-you-copied-module.dll'
    Copy-Item 'C:\Program Files\dotnet\sdk\<version-number>\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll' .
    
  4. 导入模块并执行命令:

    Import-Module .\MyModule.dll
    Write-TimestampedMessage "Test message."
    

    此时命令应当成功。如果仍失败,请重启 Windows PowerShell,确保会话中没有之前加载的旧版本程序集,然后重复本步骤。

如果模块还依赖其他第三方库,可能需要做更多类似工作;本方案已在使用 System.XmlSystem.Web 类型时成功验证过。

小结

通过上述步骤,我们完成了一个用 .NET Standard 2.0 类库构建的 PowerShell 二进制模块,它的运行环境覆盖:

  • PowerShell Core:在所有受支持的操作系统上均可运行,且可以在 Linux、macOS 和 Windows 上通过 .NET Core 2.x SDK 命令行工具构建;
  • Windows PowerShell:在已更新到 .NET Framework 4.7.1 的系统(包括自带该版本的 Windows 10 秋季创造者更新)上可直接运行;在更低版本的 Windows 上,可通过复制 netstandard.dll 实现程序集修复。

如果需要图形化开发体验,仓库提供了平行的 Visual Studio 版本指南 visual-studio-simple-example.md(同样基于 .NET Standard 2.0 + PowerShellStandard.Library,并附各步骤截图)。对于希望直接引用完整 PowerShell API 而非 Standard 子集的场景,仓库的 Microsoft.PowerShell.SDK 元项目文档说明了如何将 PowerShell 子项目打包为 SDK 供外部引用。选择哪条路线,取决于你需要的 API 面和需要兼容的 PowerShell 版本。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384