Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Di recente ho avuto un'idea per il modulo che volevo implementare come modulo binario. Devo ancora crearne uno usando la libreria standard di PowerShell in modo che questa fosse una buona opportunità. Ho usato la guida Creazione di un modulo binario multipiattaforma per creare questo modulo senza ostacoli. Stiamo per seguire lo stesso processo e aggiungerò qualche commento extra lungo il percorso.
Nota
La versione originale di questo articolo è apparsa nel blog scritto da @KevinMarquette. Il team di PowerShell ringrazia Kevin per aver condiviso questo contenuto con noi. Consultare il suo blog all'indirizzo PowerShellExplained.com.
Che cos'è la libreria standard di PowerShell?
La libreria standard di PowerShell consente di creare moduli multipiattaforma che funzionano sia in PowerShell che in Windows PowerShell 5.1.
Perché i moduli binari?
Quando si scrive un modulo in C# si concede l'accesso semplice ai cmdlet e alle funzioni di PowerShell. Tuttavia, se si crea un modulo che non dipende da molti altri comandi di PowerShell, il vantaggio delle prestazioni può essere significativo. PowerShell è stato ottimizzato per l'amministratore, non per il computer. Passando a C#, si elimina il sovraccarico aggiunto da PowerShell.
Ad esempio, è disponibile un processo critico che esegue molte operazioni con JSON e tabelle hash. PowerShell è stato ottimizzato per quanto possibile, ma il processo richiede ancora 12 minuti. Il modulo contiene già un sacco di PowerShell in stile C#. In questo modo la conversione in un modulo binario è pulita e semplice. Convertendo in un modulo binario, il tempo di processo è stato ridotto da oltre 12 minuti a meno di quattro minuti.
Moduli ibridi
È possibile combinare cmdlet binari con funzioni avanzate di PowerShell. Tutto ciò che si conosce sui moduli di script si applica allo stesso modo. Il file di psm1 vuoto è incluso in modo da poter aggiungere altre funzioni di PowerShell in un secondo momento.
Quasi tutti i cmdlet compilati creati sono stati creati prima come funzioni di PowerShell. Tutti i moduli binari sono moduli realmente ibridi.
Script di compilazione
Ho mantenuto lo script di compilazione semplice qui. In genere utilizzo uno script Invoke-Build grande come parte della mia pipeline CI/CD. Fa più magia come eseguire test Pester, eseguendo PSScriptAnalyzer, controllo delle versioni e pubblicazione in PSGallery. Dopo aver iniziato a usare uno script di compilazione per i moduli, è stato possibile trovare molte cose da aggiungere.
Pianificazione del modulo
Il piano per questo modulo consiste nel creare una cartella src per il codice C# e strutturare il resto come per un modulo script. Ciò include l'uso di uno script di compilazione per compilare tutti gli elementi in una cartella Output. La struttura di cartelle è simile alla seguente:
MyModule
├───src
├───Output
│ └───MyModule
├───MyModule
│ ├───Data
│ ├───Private
│ └───Public
└───Tests
Iniziare
Prima di tutto è necessario creare la cartella e creare il repository Git. Uso $module come segnaposto per il nome del modulo. In questo modo sarà più semplice riutilizzare questi esempi, se necessario.
$module = 'MyModule'
New-Item -Path $module -Type Directory
Set-Location $module
git init
Creare quindi le cartelle a livello radice.
New-Item -Path 'src' -Type Directory
New-Item -Path 'Output' -Type Directory
New-Item -Path 'Tests' -Type Directory
New-Item -Path $module -Type Directory
Configurazione del modulo binario
Questo articolo è incentrato sul modulo binario, quindi cominceremo da lì. Questa sezione estrae esempi dalla guida Creazione di un modulo binario multipiattaforma. Consultare questa guida se sono necessari altri dettagli o se si verificano problemi.
Prima di tutto, è necessario controllare la versione di dotnet core SDK installata. Sto usando la versione 2.1.4, ma dovresti avere 2.0.0 o versione successiva prima di continuare.
PS> dotnet --version
2.1.4
Sto lavorando fuori dalla cartella src per questa sezione.
Set-Location 'src'
Usando il comando dotnet, creare una nuova libreria di classi.
dotnet new classlib --name $module
Non voglio che il progetto di libreria sia creato in una sottocartella, perché non voglio questo livello aggiuntivo di annidamento. Sto per spostare i file su un livello superiore.
Move-Item -Path .\$module\* -Destination .\
Remove-Item $module -Recurse
Impostare la versione di .NET Core SDK per il progetto. Ho l'SDK 2.1, quindi sto per specificare 2.1.0.
Utilizza 2.0.0 se stai utilizzando l'SDK 2.0.
dotnet new globaljson --sdk-version 2.1.0
Aggiungere il pacchetto NuGet della libreria standard di PowerShell al progetto. Assicurarsi di usare la versione più recente disponibile per il livello di compatibilità necessario. L'impostazione predefinita è la versione più recente, ma non credo che questo modulo sfrutta funzionalità più recenti di PowerShell 3.0.
dotnet add package PowerShellStandard.Library --version 7.0.0-preview.1
Dovrebbe essere presente una cartella src simile alla seguente:
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
Ora siamo pronti ad aggiungere il nostro codice al progetto.
Compilazione di un cmdlet binario
È necessario aggiornare il src\Class1.cs per contenere questo cmdlet starter:
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();
}
}
}
Rinominare il file in modo che corrisponda al nome della classe.
Rename-Item .\Class1.cs .\ResolveMyCmdletCommand.cs
È quindi possibile compilare il modulo.
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
È possibile chiamare Import-Module nella nuova DLL per caricare il nuovo 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
Se l'importazione non riesce nel sistema, provare ad aggiornare .NET alla versione 4.7.1 o successiva. La guida Creazione di un modulo binario multipiattaforma fornisce altre informazioni sul supporto e sulla compatibilità di .NET per le versioni precedenti di .NET.
Manifesto del modulo
È interessante importare la DLL e avere un modulo funzionante. Mi piace continuare a usarlo e creare un manifesto del modulo. È necessario il manifesto se si vuole pubblicare in PSGallery in un secondo momento.
Dalla radice del progetto è possibile eseguire questo comando per creare il manifesto del modulo necessario.
$manifestSplat = @{
Path = ".\$module\$module.psd1"
Author = 'Kevin Marquette'
NestedModules = @('bin\MyModule.dll')
RootModule = "$module.psm1"
FunctionsToExport = @('Resolve-MyCmdlet')
}
New-ModuleManifest @manifestSplat
Si creerà anche un modulo radice vuoto per le future funzioni di PowerShell.
Set-Content -Value '' -Path ".\$module\$module.psm1"
In questo modo è possibile combinare sia le normali funzioni di PowerShell che i cmdlet binari nello stesso progetto.
Compilazione del modulo completo
Compilo tutti gli elementi insieme in una cartella di output. A tale scopo, è necessario creare uno script di compilazione. In genere lo aggiungerei a uno script di Invoke-Build, ma è possibile mantenerlo semplice per questo esempio. Aggiungerlo a un build.ps1 nella radice del progetto.
$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"
Questi comandi compilano la DLL e la inseriscono nella cartella output\$module\bin. Copia quindi gli altri file di moduli nella posizione prevista.
Output
└───MyModule
├───MyModule.psd1
├───MyModule.psm1
└───bin
├───MyModule.deps.json
├───MyModule.dll
└───MyModule.pdb
A questo punto, è possibile importare il modulo con il file psd1.
Import-Module ".\Output\$module\$module.psd1"
Da qui è possibile inserire la cartella .\Output\$module nella directory $Env:PSModulePath, che carica automaticamente il comando ogni volta che è necessario.
Aggiornamento: dotnet new PSModule
Ho imparato che lo strumento dotnet ha un modello di PSModule.
Tutti i passaggi descritti in precedenza sono ancora validi, ma questo modello ne elimina molti. È ancora un modello abbastanza nuovo che sta ancora subendo qualche ritocco. Aspettatevi di continuare a migliorare da qui.
Questo è il modo in cui si installa e si utilizza il modello 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
Questo modello minimamente funzionante si occupa dell'aggiunta di .NET SDK, della libreria standard di PowerShell e crea una classe di esempio nel progetto. È possibile compilarlo ed eseguirlo subito.
Dettagli importanti
Prima di terminare questo articolo, ecco alcuni altri dettagli che vale la pena menzionare.
Scaricamento di DLL
Una volta caricato un modulo binario, non è possibile scaricarlo. Il file DLL viene bloccato fino a quando non viene scaricato. Questo può essere fastidioso durante lo sviluppo perché ogni volta che si apporta una modifica e si vuole compilarlo, il file è spesso bloccato. L'unico modo affidabile per risolvere questo problema consiste nel chiudere la sessione di PowerShell che ha caricato la DLL.
Azione della finestra di ricaricamento di VS Code
La maggior parte delle attività di sviluppo di PowerShell viene eseguita in VS Code. Quando si lavora su un modulo binario (o un modulo con classi), si è ottenuto l'abitudine di ricaricare VS Code ogni volta che si compila.
CTRL+MAIUSC+P si apre la finestra di comando e Reload Window è sempre nella parte superiore dell'elenco.
Sessioni di PowerShell annidate
Un'altra opzione consiste nell'avere una buona copertura dei test Pester. È quindi possibile modificare lo script build.ps1 per avviare una nuova sessione di PowerShell, eseguire la compilazione, eseguire i test e chiudere la sessione.
Aggiornamento dei moduli installati
Questo blocco può essere fastidioso quando si tenta di aggiornare il modulo installato in locale. Se una sessione l'ha caricata, devi rintracciarla e chiuderla. Questo è meno problematico durante l'installazione da PSGallery perché il controllo delle versioni del modulo inserisce quello nuovo in una cartella diversa.
È possibile configurare una PSGallery locale e pubblicare su di essa come parte del processo di build. Quindi fai la tua installazione locale da quella PSGallery. Sembra un sacco di lavoro, ma questo può essere semplice come l'avvio di un contenitore Docker. Esploro un modo per farlo nel mio articolo intitolato Using a NuGet server for a PSRepository.
Pensieri finali
Non ho toccato la sintassi C# per la creazione di un cmdlet, ma c'è un sacco di documentazione su di esso nel Windows PowerShell SDK. È sicuramente qualcosa che vale la pena sperimentare come un passo avanti in C# più grave.