Program Tip

내 스크립트에서 사용할 사용자 지정 형식을 PowerShell에서 만들려면 어떻게해야합니까?

programtip 2020. 10. 25. 12:47
반응형

내 스크립트에서 사용할 사용자 지정 형식을 PowerShell에서 만들려면 어떻게해야합니까?


일부 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 
}

이와 같은 것이 가능하거나 PowerShell에서 권장됩니까?


PowerShell 3 이전

PowerShell의 Extensible Type System은 원래 매개 변수에서 수행 한 방식에 대해 테스트 할 수있는 구체적인 유형을 만들 수 있도록 허용하지 않았습니다. 해당 테스트가 필요하지 않은 경우 위에서 언급 한 다른 방법을 사용해도됩니다.

예제 스크립트에서와 같이 캐스트하거나 형식 검사 할 수있는 실제 형식을 원한다면 C # 또는 VB.net으로 작성하고 컴파일하지 않으면 수행 할 수 없습니다 . PowerShell 2에서는 "Add-Type"명령을 사용하여 매우 간단하게 수행 할 수 있습니다.

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

역사적 참고 사항 : PowerShell 1에서는 훨씬 더 어려웠습니다. CodeDom을 수동으로 사용해야했습니다. PoshCode.org에는 매우 오래된 함수 new-struct 스크립트가 있습니다. 귀하의 예는 다음과 같습니다.

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

Add-Type또는 사용 New-Struct하면 실제로 수업을 테스트하고 등을 param([Contact]$contact)사용하여 새 수업을 만들 $contact = new-object Contact수 있습니다.

PowerShell 3에서

캐스팅 할 수있는 "실제"클래스가 필요하지 않은 경우 Steven과 다른 사람들이 위에서 보여준 Add-Member 방식을 사용할 필요가 없습니다 .

PowerShell 2부터 New-Object에 -Property 매개 변수를 사용할 수 있습니다.

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

그리고 PowerShell 3에서는 PSCustomObject가속기를 사용하여 TypeName을 추가 할 수 있습니다.

[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 
}

PowerShell 5에서

PowerShell을 5 명 모든 변경, 우리는 마침내 도착에서 classenum유형을 정의하기위한 언어 키워드 (어떤 거기의로 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에서 사용자 지정 형식을 만들 수 있습니다.
Kirk Munro에는 실제로 프로세스를 자세히 설명하는 두 개의 훌륭한 게시물이 있습니다.

Manning의 Windows PowerShell In Action에는 사용자 지정 형식을 만들기 위해 도메인 별 언어를 만드는 코드 샘플도 있습니다. 이 책은 모든면에서 훌륭하기 때문에 정말 추천합니다.

위의 작업을 빠르게 수행하는 방법을 찾고 있다면 다음과 같은 사용자 지정 개체를 만드는 함수를 만들 수 있습니다.

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
}

다음은 바로 가기 방법입니다.

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

Steven Murawski의 대답은 훌륭하지만 더 짧은 것이 좋습니다 (또는 멤버 추가 구문을 사용하는 대신 깔끔한 선택 객체).

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
}

놀랍게도 아무도 사용자 지정 개체를 만들기위한이 간단한 옵션 (3 이상)을 언급하지 않았습니다.

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

유형은 실제 사용자 정의 유형이 아니라 PSCustomObject입니다. 그러나 이것은 아마도 사용자 정의 개체를 만드는 가장 쉬운 방법 일 것입니다.


사용할 수있는 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에로드하여 직접 사용하는 것입니다.

이 동작은 다른 스크립트 나 스크립트의 섹션이 실제 개체와 함께 작동 할 수 있기 때문에 권장됩니다.


사용자 지정 형식을 만들고 컬렉션에 저장하는 하드 경로는 다음과 같습니다.

$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

여기에서 언급 된 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. 속성 값을 업데이트하기 위해 mutator 호출을 입력합니다. $p.SetGivenName('Dan')
  6. 업데이트 된 값을 다시 확인하십시오. $p | Format-Table -AutoSize

설명

  • PS1XML 파일을 사용하면 유형에 대한 사용자 정의 속성을 정의 할 수 있습니다.
  • It is not restricted to .net types as the documentation implies; so you can put what you like in '/Types/Type/Name' any object created with a matching 'PSTypeName' will inherit the members defined for this type.
  • Members added through PS1XML or Add-Member are restricted to NoteProperty, AliasProperty, ScriptProperty, CodeProperty, ScriptMethod, and CodeMethod (or PropertySet/MemberSet; though those are subject to the same restrictions). All of these properties are read only.
  • By defining a ScriptMethod we can cheat the above restriction. E.g. We can define a method (e.g. Initialize) which creates new properties, setting their values for us; thus ensuring our object has all the properties we need for our other scripts to work.
  • We can use this same trick to allow the properties to be updatable (albeit via method rather than direct assignment), as shown in the example's SetGivenName.

This approach isn't ideal for all scenarios; but is useful for adding class-like behaviors to custom types / can be used in conjunction with other methods mentioned in the other answers. E.g. in the real world I'd probably only define the FullName property in the PS1XML, then use a function to create the object with the required values, like so:

More Info

Take a look at the documentation, or the OOTB type file Get-Content $PSHome\types.ps1xml for inspiration.

# 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

참고URL : https://stackoverflow.com/questions/59819/how-do-i-create-a-custom-type-in-powershell-for-my-scripts-to-use

반응형