내 스크립트에서 사용할 사용자 지정 형식을 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 명 모든 변경, 우리는 마침내 도착에서 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에서 사용자 지정 형식을 만들 수 있습니다.
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 또는 필요).
예
- 유형을 정의 하는 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>
- 유형 가져 오기 :
Update-TypeData -AppendPath .\Person.Types.ps1xml
- 사용자 지정 유형의 개체를 만듭니다.
$p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
- XML에서 정의한 스크립트 메소드를 사용하여 유형을 초기화하십시오.
$p.Initialize('Anne', 'Droid')
- 보세요. 정의 된 모든 속성이 표시됩니다.
$p | Format-Table -AutoSize
- 속성 값을 업데이트하기 위해 mutator 호출을 입력합니다.
$p.SetGivenName('Dan')
- 업데이트 된 값을 다시 확인하십시오.
$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
orAdd-Member
are restricted toNoteProperty
,AliasProperty
,ScriptProperty
,CodeProperty
,ScriptMethod
, andCodeMethod
(orPropertySet
/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
'Program Tip' 카테고리의 다른 글
텍스트 파일에서 n 번째 열 가져 오기 (0) | 2020.10.25 |
---|---|
IntelliJ 새 프로젝트-Maven Archetype 목록이 비어 있음 (0) | 2020.10.25 |
WPF DataGrid에서 ComboBoxColumn의 ItemsSource 바인딩 (0) | 2020.10.25 |
Java에서 부울 배열 채우기 (0) | 2020.10.25 |
Hudson과 CruiseControl for Java 프로젝트의 차이점은 무엇입니까? (0) | 2020.10.25 |