मैं अपनी स्क्रिप्ट के उपयोग के लिए PowerShell में एक कस्टम प्रकार कैसे बनाऊं?


88

मैं अपनी कुछ PowerShell लिपियों में कस्टम प्रकार को परिभाषित और उपयोग करने में सक्षम होना चाहूंगा। उदाहरण के लिए, आइए दिखाते हैं कि मुझे एक ऐसी वस्तु की आवश्यकता थी जिसमें निम्नलिखित संरचना थी:

Contact
{
    string First
    string Last
    string Phone
}

मैं इसे बनाने के बारे में कैसे जाऊंगा ताकि मैं इसे निम्नलिखित की तरह फ़ंक्शन में उपयोग कर सकूं:

function PrintContact
{
    param( [Contact]$contact )
    "Customer Name is " + $contact.First + " " + $contact.Last
    "Customer Phone is " + $contact.Phone 
}

क्या ऐसा कुछ संभव है, या यहां तक ​​कि पॉवरशेल में भी सिफारिश की गई है?

जवाबों:


135

पॉवरशेल 3 से पहले

पॉवरशेल के एक्स्टेंसिबल टाइप सिस्टम ने मूल रूप से आपको ठोस प्रकार नहीं बनाने दिया, जो आप अपने पैरामीटर में किए गए तरीके के खिलाफ परीक्षण कर सकते हैं। यदि आपको उस परीक्षण की आवश्यकता नहीं है, तो आप ऊपर बताए गए किसी भी अन्य तरीके से ठीक हैं।

यदि आप एक वास्तविक प्रकार चाहते हैं, जिसे आप अपनी उदाहरण स्क्रिप्ट में ... के साथ टाइप या चेक कर सकते हैं ... इसे C # या VB.net में लिखे बिना और संकलन के बिना नहीं किया जा सकता है। PowerShell 2 में, आप "सिम-टाइप" कमांड का उपयोग इसे काफी अनुकरणीय करने के लिए कर सकते हैं:

add-type @"
public struct contact {
   public string First;
   public string Last;
   public string Phone;
}
"@

ऐतिहासिक नोट : पॉवरशेल 1 में यह और भी कठिन था। आपको मैन्युअल रूप सेकोडडोम काउपयोग करना था,पॉशकोड परएक बहुत पुरानी फ़ंक्शन नई-संरचना स्क्रिप्ट है जो मदद करेगी। आपका उदाहरण बन जाता है:

New-Struct Contact @{
    First=[string];
    Last=[string];
    Phone=[string];
}

का उपयोग करना Add-Typeया New-Structआप वास्तव में अपने में कक्षा का परीक्षण करने देंगे param([Contact]$contact)और नए उपयोग कर सकते हैं $contact = new-object Contactऔर इसी तरह ...

PowerShell 3 में

यदि आपको एक "वास्तविक" वर्ग की आवश्यकता नहीं है, जिसे आप कर सकते हैं, तो आपको ऐड-मेंबर के तरीके का उपयोग करने की आवश्यकता नहीं है जिसे स्टीवन और अन्य लोगों ने ऊपर दिखाया है।

PowerShell 2 के बाद से आप नए ऑब्जेक्ट के लिए -Property पैरामीटर का उपयोग कर सकते हैं:

$Contact = New-Object PSObject -Property @{ First=""; Last=""; Phone="" }

और PowerShell 3 में, हमें PSCustomObjectटाइपनेम जोड़ने के लिए त्वरक का उपयोग करने की क्षमता मिली:

[PSCustomObject]@{
    PSTypeName = "Contact"
    First = $First
    Last = $Last
    Phone = $Phone
}

आप अभी भी केवल एक ही वस्तु प्राप्त कर रहे हैं, इसलिए आपको यह सुनिश्चित New-Contactकरने के लिए एक फ़ंक्शन बनाना चाहिए कि प्रत्येक वस्तु समान निकले, लेकिन अब आप आसानी से एक पैरामीटर को सत्यापित कर सकते हैं "यह एक प्रकार है जिसमें एक पैरामीटर को PSTypeNameविशेषता के साथ सजाया गया है:

function PrintContact
{
    param( [PSTypeName("Contact")]$contact )
    "Customer Name is " + $contact.First + " " + $contact.Last
    "Customer Phone is " + $contact.Phone 
}

पॉवरशेल 5 में

PowerShell 5 में सब कुछ बदल जाता है, और हम अंततः मिल गए classऔर enumभाषा के रूप में टाइपिंग परिभाषित करने के लिए कीवर्ड (यह नहीं है structलेकिन यह ठीक है)

class Contact
{
    # Optionally, add attributes to prevent invalid values
    [ValidateNotNullOrEmpty()][string]$First
    [ValidateNotNullOrEmpty()][string]$Last
    [ValidateNotNullOrEmpty()][string]$Phone

    # optionally, have a constructor to 
    # force properties to be set:
    Contact($First, $Last, $Phone) {
       $this.First = $First
       $this.Last = $Last
       $this.Phone = $Phone
    }
}

हमें बिना उपयोग के ऑब्जेक्ट बनाने का एक नया तरीका भी मिला है New-Object: [Contact]::new()- वास्तव में, यदि आपने अपनी कक्षा को सरल रखा है और एक कंस्ट्रक्टर को परिभाषित नहीं करते हैं, तो आप हैशटेबल को कास्टिंग करके ऑब्जेक्ट बना सकते हैं (हालांकि बिना कंस्ट्रक्टर के, कोई रास्ता नहीं होगा लागू करने के लिए कि सभी संपत्तियों को सेट किया जाना चाहिए):

class Contact
{
    # Optionally, add attributes to prevent invalid values
    [ValidateNotNullOrEmpty()][string]$First
    [ValidateNotNullOrEmpty()][string]$Last
    [ValidateNotNullOrEmpty()][string]$Phone
}

$C = [Contact]@{
   First = "Joel"
   Last = "Bennett"
}

बहुत बढ़िया जवाब! केवल इस बात को ध्यान में रखते हुए कि यह शैली लिपियों के लिए बहुत आसान है और अभी भी PowerShell 5 में काम करता है: नई-वस्तु PSObject -प्रोपरेट्टी @ {प्रोप यहाँ ...}
रयान

2
शुरुआती पॉवरशेल 5 रिलीज़ में आप क्लास-सिंटैक्स का उपयोग करके बनाई गई कक्षाओं के साथ नई-वस्तु का उपयोग नहीं कर सकते थे, लेकिन अब आप कर सकते हैं। फिर भी, यदि आप क्लास कीवर्ड का उपयोग कर रहे हैं, तो आपकी स्क्रिप्ट वैसे भी PS5 तक ही सीमित है, इसलिए मैं तब भी सिफारिश करूंगा: यदि ऑब्जेक्ट का कंस्ट्रक्टर (पैरामीटर न्यू-ऑब्जेक्ट की तुलना में बहुत तेज है) तो नए सिंटैक्स का उपयोग किया जाता है या अन्यथा कास्टिंग, जो क्लीनर सिंटैक्स और तेज दोनों है।
जयकुल

क्या आप सुनिश्चित हैं कि प्रकार की जाँच का उपयोग करके बनाए गए प्रकारों के साथ नहीं किया जा सकता है Add-Type? यह 2008 Win 2008 R2 पर PowerShell 2 में काम करने लगता है। कहें कि मैं आपके उत्तर के रूप में contactउपयोग कर रहा हूं Add-Typeऔर फिर एक उदाहरण बनाता हूं $con = New-Object contact -Property @{ First="a"; Last="b"; Phone="c" }:। फिर बुला इस समारोह काम करता है: function x([contact]$c) { Write-Host ($c | Out-String) $c.GetType() }, लेकिन इस कार्यप्रणाली को कॉल विफल रहता है, x([doesnotexist]$c) { Write-Host ($c | Out-String) $c.GetType() }। कॉलिंग x 'abc'भी कास्टिंग के बारे में एक उचित त्रुटि संदेश के साथ विफल हो जाती है। PS 2 और 4. में परीक्षण किया गया
jpmc26

बेशक आप Add-Type@ jpmc26 के साथ बनाए गए प्रकारों की जांच कर सकते हैं , मैंने जो कहा है वह यह है कि आप इसे संकलन के बिना नहीं कर सकते (यानी: इसे C # में लिखे और कॉल किए बिना Add-Type)। निश्चित रूप से, PS3 से आप कर सकते हैं - एक [PSTypeName("...")]विशेषता है जो आपको एक स्ट्रिंग के रूप में प्रकार निर्दिष्ट करने की अनुमति देती है, जो पीएससीटाइप ओब्जेक्ट्स के खिलाफ PSTypeNames सेट के खिलाफ परीक्षण का समर्थन करता है ...
Jaykul

58

PowerShell में कस्टम प्रकार का निर्माण किया जा सकता है।
किर्क मुनरो में वास्तव में दो महान पद हैं जो प्रक्रिया को पूरी तरह से विस्तार देते हैं।

मैनिंग द्वारा पुस्तक विंडोज पावरशेल इन एक्शन में कस्टम प्रकार बनाने के लिए एक डोमेन विशिष्ट भाषा बनाने के लिए एक कोड नमूना भी है। पुस्तक चारों ओर से उत्कृष्ट है, इसलिए मैं वास्तव में इसकी सलाह देता हूं।

यदि आप उपर्युक्त करने का कोई त्वरित तरीका ढूंढ रहे हैं, तो आप कस्टम ऑब्जेक्ट बनाने के लिए एक फ़ंक्शन बना सकते हैं जैसे

function New-Person()
{
  param ($FirstName, $LastName, $Phone)

  $person = new-object PSObject

  $person | add-member -type NoteProperty -Name First -Value $FirstName
  $person | add-member -type NoteProperty -Name Last -Value $LastName
  $person | add-member -type NoteProperty -Name Phone -Value $Phone

  return $person
}

17

यह शॉर्टकट विधि है:

$myPerson = "" | Select-Object First,Last,Phone

3
मूल रूप से, Select-Object cmdlet उन ऑब्जेक्ट में गुण जोड़ता है जो यह दिया गया है यदि ऑब्जेक्ट में पहले से ही वह गुण नहीं है। इस मामले में आप एक रिक्त स्ट्रिंग ऑब्जेक्ट को Select-Object cmdlet को सौंप रहे हैं। यह गुण जोड़ता है और पाइप के साथ ऑब्जेक्ट को पास करता है। या यदि यह पाइप में अंतिम कमांड है, तो यह ऑब्जेक्ट को आउटपुट करता है। मुझे यह इंगित करना चाहिए कि मैं केवल इस पद्धति का उपयोग करता हूं यदि मैं प्रॉम्प्ट पर काम कर रहा हूं। स्क्रिप्ट के लिए, मैं हमेशा अधिक स्पष्ट ऐड-सदस्य या न्यू-ऑब्जेक्ट cmdlets का उपयोग करता हूं।
ईबीग्रीन

हालांकि यह एक बेहतरीन ट्रिक है, जिससे आप वास्तव में इसे और भी छोटा बना सकते हैं:$myPerson = 1 | Select First,Last,Phone
RaYell

यह आपको मूल प्रकार के कार्यों का उपयोग करने की अनुमति नहीं देता है, क्योंकि यह प्रत्येक सदस्य के प्रकार को स्ट्रिंग के रूप में सेट करता है। ऊपर Jaykul योगदान को देखते हुए, एक के रूप में प्रत्येक सदस्य टिप्पणी का पता चलता है NotePropertyकी stringप्रकार, यह एक है Propertyजो कुछ भी प्रकार आप वस्तु में निर्दिष्ट किया है की। यह तेज है और हालांकि काम करता है।
mbrownnyc

यदि आप एक लंबी संपत्ति चाहते हैं, तो यह आपको समस्याएं दे सकता है, क्योंकि स्ट्रिंग में पहले से ही है और आपकी नई वस्तु को मौजूदा मूल्य मिलेगा - जो आप शायद नहीं चाहते हैं। मैं एक [int] पास करने की सलाह देता हूं, जैसा @RaYell दिखाता है।
FSCKur

9

स्टीवन मुरावस्की का जवाब बहुत अच्छा है, हालांकि मुझे कम-से-कम पसंद है (या ऐड-सदस्य सिंटैक्स का उपयोग करने के बजाय केवल एनटर सिलेक्ट-ऑब्जेक्ट है):

function New-Person() {
  param ($FirstName, $LastName, $Phone)

  $person = new-object PSObject | select-object First, Last, Phone

  $person.First = $FirstName
  $person.Last = $LastName
  $person.Phone = $Phone

  return $person
}

New-Objectजरूरत भी नहीं है। ऐसा ही करेंगे:... = 1 | select-object First, Last, Phone
रोमन कुजमिन

1
हाँ, लेकिन ऊपर ईबीग्रीन जैसा ही है - यह एक प्रकार का अजीब अंतर्निहित प्रकार बनाता है (आपके उदाहरण में यह एक इंट 32 होगा।) जैसा कि आप देखेंगे कि आपने टाइप किया है: $ व्यक्ति | ग्राम। मैं अंतर्निहित प्रकार एक PSCustomObject होने के लिए पसंद करते हैं
निक

2
मैं बात देख रहा हूं। फिर भी, intरास्ते के स्पष्ट लाभ हैं : 1) यह तेजी से काम करता है, ज्यादा नहीं, लेकिन इस विशेष कार्य के New-Personलिए अंतर 20% है; 2) यह स्पष्ट रूप से टाइप करना आसान है। उसी समय, मूल रूप से हर जगह इस दृष्टिकोण का उपयोग करते हुए, मैंने कभी कोई कमियां नहीं देखीं। लेकिन मैं मानता हूं: कुछ दुर्लभ मामले हो सकते हैं जब PSCustomObject बेहतर तरह का हो।
रोमन कुज़मिन

@RomanKuzmin क्या यह अभी भी 20% तेज है यदि आप एक वैश्विक कस्टम ऑब्जेक्ट को तुरंत हटा दें और इसे स्क्रिप्ट चर के रूप में संग्रहीत करें?
jpmc26

5

कस्टम ऑब्जेक्ट बनाने के लिए किसी ने भी इस सरल विकल्प (बनाम 3 या बाद के) का उल्लेख नहीं किया:

[PSCustomObject]@{
    First = $First
    Last = $Last
    Phone = $Phone
}

प्रकार PSCustomObject होगा, हालांकि एक वास्तविक कस्टम प्रकार नहीं। लेकिन कस्टम ऑब्जेक्ट बनाने का यह शायद सबसे आसान तरीका है।


PSObject और PSCustomObject के अंतर पर विल एंडरसन की इस ब्लॉग पोस्ट को भी देखें ।
कोडफॉक्स

@ कोडफ़ॉक्स ने देखा कि लिंक अब टूट गया है
सुपरजोस

2
@ सुपरपोजोस, संकेत के लिए धन्यवाद। मुझे पोस्ट का नया स्थान नहीं मिल पा रहा था। कम से कम पोस्ट संग्रह द्वारा समर्थित था ।
कोडफॉक्स

2
जाहिरा तौर पर ऐसा लगता है कि यहाँ एक Git किताब में बदल गया :)
सुपरजोस

4

PSObject और Add-Member की अवधारणा है जिसका आप उपयोग कर सकते हैं।

$contact = New-Object PSObject

$contact | Add-Member -memberType NoteProperty -name "First" -value "John"
$contact | Add-Member -memberType NoteProperty -name "Last" -value "Doe"
$contact | Add-Member -memberType NoteProperty -name "Phone" -value "123-4567"

यह आउटपुट इस तरह है:

[8] » $contact

First                                       Last                                       Phone
-----                                       ----                                       -----
John                                        Doe                                        123-4567

अन्य विकल्प (जो मुझे पता है) C # / VB.NET में एक प्रकार को परिभाषित करने और सीधे उपयोग के लिए PowerShell में उस विधानसभा को लोड करने के लिए है।

इस व्यवहार को निश्चित रूप से प्रोत्साहित किया जाता है क्योंकि यह अन्य स्क्रिप्ट या आपकी स्क्रिप्ट के वर्गों को वास्तविक वस्तु के साथ काम करने की अनुमति देता है।


3

यहां कस्टम प्रकार बनाने और उन्हें संग्रह में संग्रहीत करने का कठिन मार्ग है।

$Collection = @()

$Object = New-Object -TypeName PSObject
$Object.PsObject.TypeNames.Add('MyCustomType.Contact.Detail')
Add-Member -InputObject $Object -memberType NoteProperty -name "First" -value "John"
Add-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe"
Add-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "123-4567"
$Collection += $Object

$Object = New-Object -TypeName PSObject
$Object.PsObject.TypeNames.Add('MyCustomType.Contact.Detail')
Add-Member -InputObject $Object -memberType NoteProperty -name "First" -value "Jeanne"
Add-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe"
Add-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "765-4321"
$Collection += $Object

Write-Ouput -InputObject $Collection

ऑब्जेक्ट में टाइप नाम जोड़ने के साथ अच्छा स्पर्श।
o

0

यहाँ एक और विकल्प है, जो PSTypeName समाधान ने उल्लेख करने के लिए एक समान विचार का उपयोग करता है Jaykul (और इस प्रकार भी PSv3 या इसके बाद के संस्करण की आवश्यकता है)।

उदाहरण

  1. अपने प्रकार को परिभाषित करते हुए TypeName .Types.ps1xml फ़ाइल बनाएँ । जैसे Person.Types.ps1xml:
<?xml version="1.0" encoding="utf-8" ?>
<Types>
  <Type>
    <Name>StackOverflow.Example.Person</Name>
    <Members>
      <ScriptMethod>
        <Name>Initialize</Name>
        <Script>
            Param (
                [Parameter(Mandatory = $true)]
                [string]$GivenName
                ,
                [Parameter(Mandatory = $true)]
                [string]$Surname
            )
            $this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName
            $this | Add-Member -MemberType 'NoteProperty' -Name 'Surname' -Value $Surname
        </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>SetGivenName</Name>
        <Script>
            Param (
                [Parameter(Mandatory = $true)]
                [string]$GivenName
            )
            $this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName -Force
        </Script>
      </ScriptMethod>
      <ScriptProperty>
        <Name>FullName</Name>
        <GetScriptBlock>'{0} {1}' -f $this.GivenName, $this.Surname</GetScriptBlock>
      </ScriptProperty>
      <!-- include properties under here if we don't want them to be visible by default
      <MemberSet>
        <Name>PSStandardMembers</Name>
        <Members>
        </Members>
      </MemberSet>
      -->
    </Members>
  </Type>
</Types>
  1. अपने प्रकार का आयात करें: Update-TypeData -AppendPath .\Person.Types.ps1xml
  2. अपने कस्टम प्रकार की एक वस्तु बनाएं: $p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
  3. XML में आपके द्वारा बताई गई स्क्रिप्ट विधि का उपयोग करके अपने प्रकार को आरम्भ करें: $p.Initialize('Anne', 'Droid')
  4. इसे देखो; आपको सभी गुण परिभाषित दिखाई देंगे:$p | Format-Table -AutoSize
  5. प्रॉपर्टी की वैल्यू अपडेट करने के लिए म्यूटेटर टाइप करें: $p.SetGivenName('Dan')
  6. अद्यतन मान देखने के लिए इसे फिर से देखें: $p | Format-Table -AutoSize

व्याख्या

  • PS1XML फ़ाइल आपको प्रकारों पर कस्टम गुणों को परिभाषित करने की अनुमति देती है।
  • यह .net प्रकारों तक ही सीमित नहीं है क्योंकि प्रलेखन का अर्थ है; इसलिए आप जो कुछ भी पसंद करते हैं उसे 'टाइप / टाइप / नाम' में डाल सकते हैं, जो कि 'PSTypeName' के मेल से बनी कोई भी वस्तु इस प्रकार के लिए परिभाषित सदस्यों को विरासत में देगी।
  • सदस्यों के माध्यम से जोड़ा PS1XMLया Add-Memberतक ही सीमित हैं NoteProperty, AliasProperty, ScriptProperty, CodeProperty, ScriptMethod, और CodeMethod(या PropertySet/ MemberSet; उन्हीं प्रतिबंधों के अधीन हैं, हालांकि)। इन सभी गुणों को केवल पढ़ा जाता है।
  • परिभाषित करके ScriptMethodहम उपरोक्त प्रतिबंध को धोखा दे सकते हैं। उदाहरण के लिए, हम एक विधि (जैसे Initialize) को परिभाषित कर सकते हैं जो नए गुण बनाता है, हमारे लिए उनके मूल्यों को निर्धारित करता है; इस प्रकार हमारी वस्तु को सुनिश्चित करने के लिए हमारी अन्य लिपियों के लिए आवश्यक सभी गुण हैं।
  • हम उसी ट्रिक का उपयोग कर सकते हैं, जिससे प्रॉपर्टी को अप्रूव किया जा सके (प्रत्यक्ष असाइनमेंट के बजाय विधि के माध्यम से), जैसा कि उदाहरण में दिखाया गया है SetGivenName

यह दृष्टिकोण सभी परिदृश्यों के लिए आदर्श नहीं है; लेकिन वर्ग-प्रकार के व्यवहारों को कस्टम प्रकारों में जोड़ने के लिए उपयोगी है / अन्य उत्तरों में उल्लिखित अन्य तरीकों के साथ संयोजन में उपयोग किया जा सकता है। उदाहरण के लिए वास्तविक दुनिया में मैं शायद केवल FullNamePS1XML में संपत्ति को परिभाषित करूंगा, फिर आवश्यक मानों के साथ ऑब्जेक्ट बनाने के लिए एक फ़ंक्शन का उपयोग करें, जैसे:

और जानकारी

प्रेरणा के लिए प्रलेखन , या ओओटीबी प्रकार फ़ाइल पर एक नज़र डालें Get-Content $PSHome\types.ps1xml

# have something like this defined in my script so we only try to import the definition once.
# the surrounding if statement may be useful if we're dot sourcing the script in an existing 
# session / running in ISE / something like that
if (!(Get-TypeData 'StackOverflow.Example.Person')) {
    Update-TypeData '.\Person.Types.ps1xml'
}

# have a function to create my objects with all required parameters
# creating them from the hash table means they're PROPERties; i.e. updatable without calling a 
# setter method (note: recall I said above that in this scenario I'd remove their definition 
# from the PS1XML)
function New-SOPerson {
    [CmdletBinding()]
    [OutputType('StackOverflow.Example.Person')]
    Param (
        [Parameter(Mandatory)]
        [string]$GivenName
        ,
        [Parameter(Mandatory)]
        [string]$Surname
    )
    ([PSCustomObject][Ordered]@{
        PSTypeName = 'StackOverflow.Example.Person'
        GivenName = $GivenName
        Surname = $Surname
    })
}

# then use my new function to generate the new object
$p = New-SOPerson -GivenName 'Simon' -Surname 'Borg'

# and thanks to the type magic... FullName exists :)
Write-Information "$($p.FullName) was created successfully!" -InformationAction Continue

ps। VSCode का उपयोग करने वालों के लिए, आप PS1XML समर्थन जोड़ सकते हैं: code.visualstudio.com/docs/languages/…
JohnLBevan
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.