‫PowerShell 7.x - قسمت ششم - ایجاد Cmdletها توسط #C

ساخت وبلاگ
تا اینجا با کمک توابع توانستیم PowerShell را به اصطلاح extend کنیم. نوع دیگر دستورات، command letها هستند. این نوع دستورات را با کمک یک زبان دات‌نتی میتوانیم ایجاد کنیم. به این نوع دستورات complied cmdlet گفته میشود. در بیشتر مواقع با کمک advanced functionها میتوانید بیشتر کارها را انجام دهید؛ چراکه به صورت مستقیم امکان استفاده از دات‌نت را درون PowerShell دارید. اما شاید ترجیح دهید از سی‌شارپ یا دیگر زبان‌ها دات‌نتی برای ایجاد یک تابع استفاده کنید.

نحوه‌ی ایجاد یک cmdlet با کمک #C
ابتدا یک دایرکتوری جدید را ایجاد کرده و درون آن یک پروژه‌ی از نوع class library را ایجاد کنید. سپس پکیج PowerShellStandard.Library را درون پروژه ایجاد شده با کمک dotnet cli به پروژه اضافه کنید: 
mkdir ps_cmdlet_with_csharp && cd "$_"
dotnet new classlib
dotnet add package PowerShellStandard.Library
mv Class1.cs GetHelloCommand.cs
در پایان دستورات فوق، نام فایل پیش‌فرض Class1 را نیز به GetHelloCommand تغییر داده‌ایم. در ادامه محتویات فایل را اینگونه ویرایش خواهیم کرد: 
namespace ps_cmdlet_with_csharp;
using System.Management.Automation;
[Cmdlet(VerbsCommon.Get, "Hello")]
public class GetHelloCommand : PSCmdlet
{ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] public string Name { get; set; } protected override void BeginProcessing() { WriteObject("Start processing"); } protected override void ProcessRecord() { WriteObject("Hello " + Name); } protected override void EndProcessing() { WriteObject("End processing"); }
}
همانطور که مشاهده میکنید، کلاس فوق را از PSCmdlet ارث‌بری کرده‌ایم. در کل برای ایجاد یک command let، از یکی از انواع Cmdlet یا PSCmdlet میتوانیم ارث‌بری کنیم. Cmdlet یک کلاس پایه است که PSCmdlet نیز از آن ارث برده است و یکسری امکانات بیشتری را جهت تعامل با PowerShell ارائه میدهد: 
using System.Collections.ObjectModel;
using System.Management.Automation.Host;
namespace System.Management.Automation
{ public abstract class PSCmdlet : Cmdlet { protected PSCmdlet(); public PSEventManager Events { get; } public PSHost Host { get; } public CommandInvocationIntrinsics InvokeCommand { get; } public ProviderIntrinsics InvokeProvider { get; } public JobManager JobManager { get; } public JobRepository JobRepository { get; } public InvocationInfo MyInvocation { get; } public PagingParameters PagingParameters { get; } public string ParameterSetName { get; } public SessionState SessionState { get; } public PathInfo CurrentProviderLocation(string providerId); public Collection<string> GetResolvedProviderPathFromPSPath(string path, out ProviderInfo provider); public string GetUnresolvedProviderPathFromPSPath(string path); public object GetVariableValue(string name); public object GetVariableValue(string name, object defaultValue); }
}
در نهایت command let باید به یک DLL تبدیل شود؛ چون همانطور که قبلآً نیز اشاره شد، هر command let در واقع یک شیء دات‌نتی است. در ادامه پروژه جاری را بیلد کرده و توسط دستور Import-Module فایل DLL تولید شده را درون Shell ایمپورت خواهیم کرد: 
PS /> dotnet build
PS /> Import-Module ./bin/Debug/net7.0/ps_cmdlet_with_csharp.dll
سپس توسط دستور Get-Command میتوانیم مطمئن شویم که ماژول موردنظر با موفقیت ایمپورت شده‌است: 
PS /> Get-Command -Module ps_cmdlet_with_csharp
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Get-Hello 1.0.0.0 ps_cmdlet_with_csharp
همانطور که مشاهده میکنید، درون ماژول ps_cmdlet_with_csharp تنها یک command let تعریف شده‌است. درون یک namespace میتوانیم چندین command let را تعریف کنیم. به عنوان مثال برای مثال قبل میتوانیم: 
namespace ps_cmdlet_with_csharp;
using System.Management.Automation;
[Cmdlet(VerbsCommon.Get, "Hello")]
public class GetHelloCommand : PSCmdlet
{ // as before
}
[Cmdlet(VerbsCommon.Get, "Greetings")]
public class GetGreetingsCommand : PSCmdlet
{ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] public string Name { get; set; } protected override void BeginProcessing() { WriteObject("Start processing"); } protected override void ProcessRecord() { WriteObject("Greetings " + Name); } protected override void EndProcessing() { WriteObject("End processing"); }
}
اکنون اگر پروژه را بیلد کنیم و ماژول بیلد شده را ایمپورت کنیم، با خطای زیر مواجه خواهیم شد: 
Import-Module: Invalid assembly public key.
برای رفع این مشکل، فایل csproject را باز کرده و خط زیر را به آن اضافه کنید: 
<Project Sdk="Microsoft.NET.Sdk"> <!-- other tags --> <ItemGroup> <Compile Include="./GetHelloCommand.cs" /> </ItemGroup>
</Project>
اکنون پروژه با موفقیت بیلد و ایمپورت خواهد شد: 
PS /> Get-Command -Module ps_cmdlet_with_csharp
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Get-Greetings 1.0.0.0 ps_cmdlet_with_csharp
Cmdlet Get-Hello 1.0.0.0 ps_cmdlet_with_csharp

یک مثال تکمیلی
درون یک کلاس Cmdlet، امکان استفاده از تمامی annotationهایی را که در قسمت قبل بررسی کردیم، اینجا نیز در اختیار داریم؛ بنابراین نیاز به توضیح مجدد آن نیست. در ادامه میخواهیم یک دستور را با عنوان Push-SlackMessage تهیه کنیم که کار ارسال یک پیام را به یک کانال Slack، انجام میدهد: 
namespace ps_cmdlet_with_csharp;
using System.Management.Automation;
using System.Net.Http.Headers;
[Cmdlet(VerbsCommon.Push, "SlackMessage")]
[Alias("ssm")]
[OutputType(typeof(string))]
public class SlackMessageCommand : PSCmdlet
{ [Parameter(Mandatory = true)] [Alias("m")] public string Message { get; set; } [Parameter(Mandatory = true)] [Alias("t")] [ValidatePattern(@"xoxp-[0-9]{11}-[0-9]{12}-[0-9]{13}-[0-9a-zA-Z]{32}")] public string Token { get; set; } [Parameter(Mandatory = true)] [Alias("cid")] public string ChannelID { get; set; } protected async override void ProcessRecord() { base.ProcessRecord(); try { using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.Token); var values = new Dictionary<string, string> { { "channel", this.ChannelID }, { "text", this.Message } }; var content = new FormUrlEncodedContent(values); var response = await client.PostAsync("https://slack.com/api/chat.postMessage", content); var responseString = await response.Content.ReadAsStringAsync(); WriteObject("Message sent"); } catch (Exception ex) { WriteObject(ex.Message); } }
}

یک نکته در مورد ایمپورت کردن ماژول‌ها
دستوراتی که تاکنون ایجاد کردیم (هم توابع و هم compiled cmletها) برای اجرا باید یکبار درون حافظه قرار بگیرند و سپس امکان اجرای آنها را خواهیم داشت. دلیل آن نیز این است که همه چیز درون سشن جاری انجام خواهد شد و به محض بستن آن، تغییرات نیز ار حافظه خارج خواهند شد. یعنی برای command letی که ایجاد کردیم، با هربار باز کردن یک سشن جدید مجبور خواهیم بود که مجدداً آن را ایمپورت کنیم. برای رفع این مشکل میتوانیم از پروفایل‌ها استفاده کنیم. توسط پروفایل، امکان سفارشی‌سازی شل را خواهیم داشت. پروفایل در واقع یک اسکریپت PowerShell است که به محض اجرای PowerShell فراخوانی خواهد شد. بنابراین درون پروفایل این فرصت را خواهیم داشت تا متغیرها، ماژول‌ها، aliaseها و… را قبل از باز کردن شل، درون سشن PowerShell بارگذاری کنیم. PowerShell از چندین نوع پروفایل پشتیبانی میکند و توسط متغیر خودکار PROFILE$ میتوانیم لیست مسیرهای پروفایل‌ها را مشاهده کنیم:  
PS /> $PROFILE | Get-Member -Type NoteProperty | Select-Object Name, Definitionbject Name, Definition
Name Definition
---- ----------
AllUsersAllHosts string AllUsersAllHosts=/usr/local/microsoft/powershell/7/…
AllUsersCurrentHost string AllUsersCurrentHost=/usr/local/microsoft/powershell…
CurrentUserAllHosts string CurrentUserAllHosts=/Users/sirwanafifi/.config/powe…
CurrentUserCurrentHost string CurrentUserCurrentHost=/Users/sirwanafifi/.config/p…
برای ویرایش هر کدام از پروفایل‌های موردنظر میتوانید اینگونه عمل کنید: 
$SlackProjectPath = "/Users/sirwanafifi/Desktop/ps_cmdlet_with_csharp/bin/Debug/net7.0/ps_cmdlet_with_csharp.dll"
Import-Module $SlackProjectPath
اکنون با هر بار باز کردن PowerShell به command let جدید دسترسی خواهید داشت: 
PS /> Get-Command -Module ps_cmdlet_with_csharp
CommandType Name Version Source
----------- ---- ------- ----
Cmdlet Push-SlackMessage 1.0.0.0 ps_…
دانلود نرم افزار و برنامه...
ما را در سایت دانلود نرم افزار و برنامه دنبال می کنید

برچسب : نویسنده : دانلودی dld بازدید : 125 تاريخ : چهارشنبه 25 آبان 1401 ساعت: 14:10

خبرنامه