我纪念我想要实现为二进制模块的模块。我还没有创造一个使用的 电源外壳标准库 所以这觉得很好。我能用它 创建跨平台二进制模块 在没有任何障碍的情况下创建此模块的指南。我们将走同样的过程,我将在路上添加一些额外的评论。

-Update: - 我找到了一种更简单的方法来做一些二进制模块步骤,我将它们添加到文章的末尾。

指数

Powershell标准图书馆是什么?

电源外壳标准库允许我们创建在PowerShell核心和Windows PowerShell 5.1(或3.0)中工作的跨平台模块。

规划我们的模块

The plan for this module is to create a src folder for the C# code and structure the rest of the module like I would for a script module. This includes using a build script to compile everything into an output folder. The folder structure will look like this:

MyModule
├───src
├───Output
│   └───MyModule
├───MyModule
│   ├───Data
│   ├───Private
│   └───Public
└───Tests

入门

我通常使用膏药模板,但我的当前模板尚未有任何二进制模块支持。不是大不了的,我会用这次用手创造这个。

First I need to create the folder and create the git repo. I will be using $module as a place holder for the module name. This should make it easier for you to reuse these examples if needed.

    $module = 'MyModule'
    New-Item -Path $module -Type Directory
    Set-Location $module
    git init

然后创建根级文件夹。

    New-Item -Path 'src' -Type Directory
    New-Item -Path 'Output' -Type Directory
    New-Item -Path 'Tests' -Type Directory
    New-Item -Path $module -Type Directory

二进制模块设置

这将在二进制模块上的焦点,以便我们将在哪里开始。本节从中拉动示例 创建跨平台二进制模块 指导。如果您需要更多详细信息或有任何问题,请查看该指南。

我们要做的第一件事就是检查版本 dotnet core sdk. 我们已安装过。我正在使用2.1.4,但在继续之前,你应该有2.0.0或更新。

PS:> dotnet --version
2.1.4

I will be working out of the src folder for this section.

    Set-Location 'src'

使用dotnet命令,创建一个新的类库。

    dotnet new classlib --name $module

这在子文件夹中创建了库项目,但我不希望额外的嵌套级别。我将要将这些文件提升到一个级别。

    Move-Item -Path .\$module\* -Destination .\
    Remove-Item $module -Recurse

为项目设置.NET Core SDK版本。我有2.1 SDK,所以我要指定2.1.0。如果您使用的是2.0 SDK,请使用2.0.0。

dotnet new globaljson --sdk-version 2.1.0

添加 电源外壳标准库 package to the project. Make sure you use the most recent version available for the level of compatibility that you need. I would default to version 5.1.0-preview-05 but I don’t think this module will leverage anything newer than what PS 3.0 provides.

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

我们应该有一个SRC文件夹,如下所示:

PS:> Get-ChildItem
    Directory: \MyModule\src

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        7/14/2018   9:51 PM                obj
-a----        7/14/2018   9:51 PM             86 Class1.cs
-a----        7/14/2018  10:03 PM            259 MyModule.csproj
-a----        7/14/2018  10:05 PM             45 global.json

我们现在准备将自己的代码添加到项目中。

构建二进制cmdlet

We need to update the src\Class1.cs to contain this starter cmdlet:

    using System;
    using System.Management.Automation;

    namespace MyModule
    {
        [Cmdlet( VerbsDiagnostic.Resolve , "MyCmdlet")]
        public class ResolveMyCmdletCommand : PSCmdlet
        {
            [Parameter(Position=0)]
            public Object InputObject { get; set; }

            protected override void EndProcessing()
            {
                this.WriteObject(this.InputObject);
                base.EndProcessing();
            }
        }
    }

我们将重命名文件以匹配类名。

    Rename-Item .\Class1.cs .\ResolveMyCmdletCommand.cs

然后我们可以构建我们的模块。

PS:> dotnet build

Microsoft (R) Build Engine version 15.5.180.51428 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

Restore completed in 18.19 ms for C:\workspace\MyModule\src\MyModule.csproj.
MyModule -> C:\workspace\MyModule\src\bin\Debug\netstandard2.0\MyModule.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.19

We can call Import-Module on the new dll and it will load our new CMDlet.

PS:> Import-Module .\bin\Debug\netstandard2.0\$module.dll
PS:> Get-Command -Module $module

CommandType Name                    Version Source
----------- ----                    ------- ------
Cmdlet      Resolve-MyCmdlet        1.0.0.0 MyModule

如果导入在系统上失败,请尝试更新.NET到4.7.1或更新。这 使用.NET核心命令行接口工具创建跨平台二进制模块 Guide更详细地了解.NET支持和较旧版本的.NET的兼容性。

模块清单

我们可以将DLL导入并具有工作模块很酷。我想继续使用它并创建模块清单。如果我们稍后想发布到Psgallery,我们将需要这个。

从我们项目的根源,我们可以运行此命令以创建我们需要的模块清单。

    $manifestSplat = @{
        Path              = ".\$module\$module.psd1"
        Author            = 'Kevin Marquette'
        NestedModules     = @('bin\MyModule.dll')
        RootModule        = "$module.psm1"
        FunctionsToExport = @('Resolve-MyCmdlet')
    }
    New-ModuleManifest @manifestSplat

我还将为未来的PowerShell函数创建一个空的根模块。

    Set-Content -Value '' -Path ".\$module\$module.psm1"

这允许我在同一项目中混合普通PowerShell函数和二进制cmdlet。

构建完整模块

I compile everything together into an output folder. We need to create a build script to do that. I would normally add this to an Invoke-Build script, but we can keep it simple for this example. Add this to a build.ps1 at the root of the project.

    $module = 'MyModule'
    Push-Location $PSScriptroot

    dotnet build $PSScriptRoot\src -o $PSScriptRoot\output\$module\bin
    Copy-Item "$PSScriptRoot\$module\*" "$PSScriptRoot\output\$module" -Recurse -Force

    Import-Module "$PSScriptRoot\Output\$module\$module.psd1"
    Invoke-Pester "$PSScriptRoot\Tests"

This will build our DLL and place it into our output\$module\bin folder. It will then copy the other module files into place.

Output
└───MyModule
    │   MyModule.psd1
    │   MyModule.psm1
    │
    └───bin
            MyModule.deps.json
            MyModule.dll
            MyModule.pdb

此时,我们能够使用PSD1文件导入模块。

    Import-Module ".\Output\$module\$module.psd1"

From here, we can drop the .\Output\$module folder into our $env:PSModulePath directory and it will auto-load our command whenever we need it.

重要细节

在我们结束本文之前,这里有一些值得一提的详细信息。

卸下DLL

一旦加载二进制模块,您无法真正卸载它。将锁定DLL文件,直到卸载它。当开发时,这可能会很烦人,因为每次更改并想要构建它时,文件通常会被锁定。解决此问题的唯一可靠方法是关闭加载DLL的PowerShell会话。

vscode重新加载窗口操作

我做了大部分Powershell开发工作 vscode.. When I am working on a binary module (or a module with classes), I have gotten myself into the habit of reloading VSCode every time I build. Ctrl + Shift + P will pop the command window and Reload Window is always at the top of my list.

嵌套的powershell会议

另一种选择是具有良好的纠缠测试覆盖。然后您可以调整build.ps1脚本以启动新的PowerShell会话,执行构建,运行测试,然后关闭会话。

更新已安装的模块

在尝试更新本地安装的模块时,此锁定可能会很烦人。如果任何会话都装满了,你必须去追捕并关闭它。从PSGallery安装时,这是较少的问题,因为模块版本控制将新的文件夹放置在另一个文件夹中。

您可以设置本地PSGallery并将其发布为构建的一部分。然后从该PSGallery执行本地安装。这听起来像很多工作,但这可以像开局码头容器一样简单。我覆盖了在我的帖子中这样做的方法 使用NuGet服务器进行PSRepository.

为什么二元模块?

我跳进了如何制作一个二进制模块,并没有提到你想要制作一个原因。实际上,您正在编写C#并放弃轻松访问PowerShell cmdlet和功能。这是我不早在二元模块转移到二元模块的重要原因。

但是,如果您正在创建一个不依赖于大量其他PowerShell命令的模块,则性能效益可能相当大。通过删除C#,您可以通过PowerShell来缩小添加的开销。 PowerShell针对管理员进行了优化,而不是计算机,并且增加了一点开销。

在工作中,我们有一个关键的过程,与JSON和HASHTABLE有很多工作。我们尽可能多地优化PowerShell,但此过程仍在运行12分钟。该模块已包含很多C#样式PowerShell。这使得转换为二元模块非常干净,易于做。我们的转换将从12分钟减少到4分钟以下。那一点睁开眼睛。

混合模块

You can mix binary Cmdlets with PowerShell advanced functions. That is exactly what I am doing in this guide. You can take everything you know about script modules and it all applies the same way. The empty psm1 file that I created today is there just so you can drop in other PowerShell functions later.

几乎所有我们创建的编译cmdlet都以PowerShell功能开始。所有的二进制模块都是混合模块。

构建脚本

I kept the build script simple here. I generally use a large Invoke-Build script as part of my CI/CD pipeline. It does more magic like running Pester tests, running PSSciptAnalyzer, managing versioning, and publishing to the PSGallery. Once I started using a build script for my modules, I was able to find lots of things to add to it.

最后的想法

二进制模块很容易创建。我没有触摸C#语法以创建cmdlet,但它有很多文档。 Windows PowerShell SDK.。这绝对是值得尝试的,作为踏脚石进入更严重的C#。

更新:Dotnet New PsModule

I learned that there is a PSModule dotnet new template.

我上面概述的所有步骤仍然有效,但这种模板会削减很多。它仍然是一个相当新的模板,仍然得到一些抛光剂。期待它从这里保持更好。

这是您使用安装的方式和使用PSModule模板。

    dotnet new -i Microsoft.PowerShell.Standard.Module.Template
    dotnet new psmodule
    dotnet build
    Import-Module "bin\Debug\netstandard2.0\$module.dll"
    Get-Module $module

此最小可行模板负责添加.NET SDK,PowerShell标准库,并在项目中创建示例类。您可以立即构建并立即运行它。