Antes de PowerShell 3
El sistema de tipo extensible de PowerShell no le permitió originalmente crear tipos concretos que pueda probar de la forma en que lo hizo en su parámetro. Si no necesita esa prueba, está bien con cualquiera de los otros métodos mencionados anteriormente.
Si desea un tipo real al que pueda enviar o verificar el tipo, como en su script de ejemplo ... no se puede hacer sin escribirlo en C # o VB.net y compilarlo. En PowerShell 2, puede usar el comando "Agregar tipo" para hacerlo de manera bastante simple:
add-type @"
public struct contact {
public string First;
public string Last;
public string Phone;
}
"@
Nota histórica : en PowerShell 1 fue aún más difícil. Tenía que usar CodeDom manualmente, hay una función muy antigua de script new-struct en PoshCode.org que ayudará. Tu ejemplo se convierte en:
New-Struct Contact @{
First=[string];
Last=[string];
Phone=[string];
}
Usar Add-Type
o New-Struct
te permitirá probar la clase en tu param([Contact]$contact)
y crear nuevas usando $contact = new-object Contact
y así sucesivamente ...
En PowerShell 3
Si no necesitas una clase "real" a la que puedas enviar contenido, no tienes que usar la forma de Agregar miembro que Steven y otros han demostrado anteriormente.
Desde PowerShell 2, puede usar el parámetro -Property para New-Object:
$Contact = New-Object PSObject -Property @{ First=""; Last=""; Phone="" }
Y en PowerShell 3, tenemos la capacidad de usar el PSCustomObject
acelerador para agregar un TypeName:
[PSCustomObject]@{
PSTypeName = "Contact"
First = $First
Last = $Last
Phone = $Phone
}
Todavía está obteniendo un único objeto, por lo que debe crear una New-Contact
función para asegurarse de que todos los objetos salgan igual, pero ahora puede verificar fácilmente que un parámetro "es" uno de esos tipos decorando un parámetro con el PSTypeName
atributo:
function PrintContact
{
param( [PSTypeName("Contact")]$contact )
"Customer Name is " + $contact.First + " " + $contact.Last
"Customer Phone is " + $contact.Phone
}
En PowerShell 5
En PowerShell 5 todo cambia, y finalmente obtuvimos class
y enum
como palabras clave de idioma para definir tipos (no hay, struct
pero está bien):
class Contact
{
[ValidateNotNullOrEmpty()][string]$First
[ValidateNotNullOrEmpty()][string]$Last
[ValidateNotNullOrEmpty()][string]$Phone
Contact($First, $Last, $Phone) {
$this.First = $First
$this.Last = $Last
$this.Phone = $Phone
}
}
También obtuvimos una nueva forma de crear objetos sin usar New-Object
: [Contact]::new()
- de hecho, si mantienes tu clase simple y no defines un constructor, puedes crear objetos lanzando una tabla hash (aunque sin un constructor, no habría forma para hacer cumplir que todas las propiedades deben establecerse):
class Contact
{
[ValidateNotNullOrEmpty()][string]$First
[ValidateNotNullOrEmpty()][string]$Last
[ValidateNotNullOrEmpty()][string]$Phone
}
$C = [Contact]@{
First = "Joel"
Last = "Bennett"
}
Add-Type
? Parece funcionar en PowerShell 2 en Win 2008 R2. Digamos que yo definocontact
el usoAdd-Type
como en su respuesta y luego crear una instancia:$con = New-Object contact -Property @{ First="a"; Last="b"; Phone="c" }
. A continuación, llamar a esta función obras:function x([contact]$c) { Write-Host ($c | Out-String) $c.GetType() }
, pero llamar a esta función falla,x([doesnotexist]$c) { Write-Host ($c | Out-String) $c.GetType() }
. La llamadax 'abc'
también falla con un mensaje de error apropiado sobre la transmisión. Probado en PS 2 y 4.Add-Type
@ jpmc26, lo que dije es que no puede hacerlo sin compilar (es decir, sin escribirlo en C # y llamarAdd-Type
). Por supuesto, desde PS3 puede: hay un[PSTypeName("...")]
atributo que le permite especificar el tipo como una cadena, que admite pruebas contra PSCustomObjects con el conjunto PSTypeNames ...La creación de tipos personalizados se puede realizar en PowerShell.
Kirk Munro en realidad tiene dos excelentes publicaciones que detallan el proceso a fondo.
El libro Windows PowerShell en acción de Manning también tiene una muestra de código para crear un lenguaje específico de dominio para crear tipos personalizados. El libro es excelente en todos los aspectos, así que realmente lo recomiendo.
Si solo está buscando una forma rápida de hacer lo anterior, puede crear una función para crear el objeto personalizado como
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 }
fuente
Este es el método de atajo:
$myPerson = "" | Select-Object First,Last,Phone
fuente
$myPerson = 1 | Select First,Last,Phone
NoteProperty
delstring
tipo, es unaProperty
de cualquier tipo que ha asignado en el objeto. Sin embargo, esto es rápido y funciona.La respuesta de Steven Murawski es excelente, sin embargo, me gusta el más corto (o más bien el select-object más ordenado en lugar de usar la sintaxis de agregar miembros):
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 }
fuente
New-Object
ni siquiera es necesario. Esto hará lo mismo:... = 1 | select-object First, Last, Phone
int
forma: 1) funciona más rápido, no mucho, pero para esta función en particularNew-Person
la diferencia es del 20%; 2) aparentemente es más fácil de escribir. Al mismo tiempo, utilizando este enfoque básicamente en todas partes, nunca he visto inconvenientes. Pero estoy de acuerdo: puede haber algunos casos raros en los que PSCustomObject sea mejor.Sorprendido, nadie mencionó esta opción simple (vs 3 o posterior) para crear objetos personalizados:
[PSCustomObject]@{ First = $First Last = $Last Phone = $Phone }
El tipo será PSCustomObject, aunque no un tipo personalizado real. Pero probablemente sea la forma más sencilla de crear un objeto personalizado.
fuente
Existe el concepto de PSObject y Add-Member que podría utilizar.
$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"
Esto genera como:
[8] » $contact First Last Phone ----- ---- ----- John Doe 123-4567
La otra alternativa (que conozco) es definir un tipo en C # / VB.NET y cargar ese ensamblado en PowerShell para usarlo directamente.
Definitivamente se recomienda este comportamiento porque permite que otros scripts o secciones de su script trabajen con un objeto real.
fuente
Aquí está el camino difícil para crear tipos personalizados y almacenarlos en una colección.
$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
fuente
Aquí hay una opción más, que usa una idea similar a la solución PSTypeName mencionada por Jaykul (y por lo tanto también requiere PSv3 o superior).
Ejemplo
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'}
$p.Initialize('Anne', 'Droid')
$p | Format-Table -AutoSize
$p.SetGivenName('Dan')
$p | Format-Table -AutoSize
Explicación
PS1XML
oAdd-Member
se limitan aNoteProperty
,AliasProperty
,ScriptProperty
,CodeProperty
,ScriptMethod
, yCodeMethod
(oPropertySet
/MemberSet
, aunque esos son sujetos a las mismas restricciones). Todas estas propiedades son de solo lectura.ScriptMethod
, podemos burlar la restricción anterior. Por ejemplo, podemos definir un método (por ejemploInitialize
) que crea nuevas propiedades, estableciendo sus valores por nosotros; asegurando así que nuestro objeto tiene todas las propiedades que necesitamos para que funcionen nuestros otros scripts.SetGivenName
.Este enfoque no es ideal para todos los escenarios; pero es útil para agregar comportamientos de clase a tipos personalizados / se puede usar junto con otros métodos mencionados en las otras respuestas. Por ejemplo, en el mundo real probablemente solo definiría la
FullName
propiedad en el PS1XML, luego usaría una función para crear el objeto con los valores requeridos, así:Más información
Eche un vistazo a la documentación o al archivo de tipo OOTB
Get-Content $PSHome\types.ps1xml
para inspirarse.# 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
fuente